shithub: riscv

Download patch

ref: 1c2fc5208163b09667482c357934cd51b5938f4f
parent: 08e433f31db74945aecfcad2755ccf274e849810
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Mar 14 17:15:16 EDT 2014

ramfs: replace with new lib9p based implementation

old ramfs had a limit on the number of files it could serve
and file size was limited to maximum allocaiton size.

the new implementation uses multiple memory chunks to back file data
in a private compactable memory pool to overcome these limits.
files can be sparse. file metadata is maintained by 9pfile data
structures of lib9p.

--- a/sys/src/cmd/ramfs.c
+++ b/sys/src/cmd/ramfs.c
@@ -2,703 +2,309 @@
 #include <libc.h>
 #include <auth.h>
 #include <fcall.h>
-
+#include <thread.h>
+#include <9p.h>
 #include <pool.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.
- */
+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 locked";
 
-enum
-{
-	OPERM	= 0x3,		/* mask of all permission types in open mode */
-	Nram	= 2048,
-	Maxsize	= 768*1024*1024,
-	Maxfdata	= 8192,
-	Maxulong= (1ULL << 32) - 1,
+enum {
+	Tdat	= 0xbabababa,	
+	Tind	= 0xdadadada,
+
+	ESIZE	= 64*1024,
 };
 
-typedef struct Fid Fid;
-typedef struct Ram Ram;
+#define MAXFSIZE ((0x7fffffffll/sizeof(Ram*))*ESIZE)
 
-struct Fid
+typedef struct Ram Ram;
+struct Ram
 {
-	short	busy;
-	short	open;
-	short	rclose;
-	int	fid;
-	Fid	*next;
-	char	*user;
-	Ram	*ram;
+	int	type;
+	int 	size;
+	Ram	**link;
+	Ram	*ent[];
 };
 
-struct Ram
+int private;
+
+void*
+ramalloc(ulong size)
 {
-	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;
-};
+	void *v;
 
-enum
+	v = sbrk(size);
+	if(v == (void*)-1)
+		return nil;
+	return v;
+}
+
+void
+rammoved(void*, void *to)
 {
-	Pexec =		1,
-	Pwrite = 	2,
-	Pread = 	4,
-	Pother = 	1,
-	Pgroup = 	8,
-	Powner =	64,
-};
+	Ram **link, **elink, *x = to;
 
-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;
+	*x->link = x;
+	if(x->type != Tind)
+		return;
 
-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);
+	link = x->ent;
+	for(elink = link + (x->size / sizeof(Ram*)); link < elink; link++)
+		if((x = *link) != nil)
+			x->link = link;
+}
 
-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*);
+void
+ramnolock(Pool*)
+{
+}
 
-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,
+Pool rampool = {
+        .name=          "ram",
+        .maxsize=       800*1024*1024,
+        .minarena=      4*1024,
+        .quantum=       32,
+        .alloc=         ramalloc,
+	.move=		rammoved,
+	.lock=		ramnolock,
+	.unlock=	ramnolock,
+        .flags=         0,
 };
 
-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)
+accessfile(File *f, int a)
 {
-	USED(a);
-	if(strncmp(s, "interrupt", 9) == 0)
-		noted(NCONT);
-	noted(NDFLT);
+	f->atime = time(0);
+	if(a & AWRITE){
+		f->mtime = f->atime;
+		f->qid.vers++;
+	}
 }
 
 void
-main(int argc, char *argv[])
+fsread(Req *r)
 {
-	Ram *r;
-	char *defmnt, *service;
-	int p[2];
-	int fd;
-	int stdio = 0;
-	int mountflags;
+	int o, n, i, count;
+	vlong top, off;
+	File *f;
+	Ram *x;
+	char *p;
 
-	service = "ramfs";
-	defmnt = "/tmp";
-	mountflags = 0;
-	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 */
-		mainmem->maxsize = (uintptr)~0;
-		break;
-	case 'D':
-		debug = 1;
-		break;
-	case 'S':
-		defmnt = 0;
-		service = EARGF(usage());
-		break;
-	case 'b':
-		mountflags |= MBEFORE;
-		break;
-	case 'c':
-		mountflags |= MCREATE;
-		break;
-	case 'a':
-		mountflags |= MAFTER;
-		break;
-	default:
-		usage();
-	}ARGEND
-	if(mountflags == 0)
-		mountflags = MREPL | MCREATE;
+	f = r->fid->file;
+	off = r->ifcall.offset;
+	count = r->ifcall.count;
 
-	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");
-		}
+	if(count == 0 || off >= f->length || f->aux == nil){
+		r->ofcall.count = 0;
+		respond(r, nil);
+		return;
 	}
 
-	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);
+	top = off + count;
+	if(top > MAXFSIZE){
+		respond(r, Ebadoff);
+		return;
 	}
-	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, mountflags, "") < 0)
-			error("mount failed");
+		
+	if(top > f->length){
+		top = f->length;
+		count = top - off;
 	}
-	exits(0);
-}
+	p = (char*)r->ofcall.data;
+	while(count > 0){
+		i = off / ESIZE;
+		o = off % ESIZE;
 
-char*
-rversion(Fid*)
-{
-	Fid *f;
+		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;
+	}
+	accessfile(f, AREAD);
 
-	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;
+	r->ofcall.count = p - (char*)r->ofcall.data;
+	respond(r, nil);
 }
 
-char*
-rauth(Fid*)
+void
+fswrite(Req *r)
 {
-	return "ramfs: no authentication required";
-}
+	int o, n, i, count;
+	Ram *x, **link;
+	vlong top, off;
+	File *f;
+	char *p;
 
-char*
-rflush(Fid *f)
-{
-	USED(f);
-	return 0;
-}
+	f = r->fid->file;
+	off = r->ifcall.offset;
+	count = r->ifcall.count;
 
-char*
-rattach(Fid *f)
-{
-	/* 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;
-}
+	if(f->mode & DMAPPEND)
+		off = f->length;
 
-char*
-clone(Fid *f, Fid **nf)
-{
-	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;
-}
+	if(count == 0){
+		r->ofcall.count = 0;
+		respond(r, nil);
+		return;
+	}
 
-char*
-rwalk(Fid *f)
-{
-	Ram *r, *fram;
-	char *name;
-	Ram *parent;
-	Fid *nf;
-	char *err;
-	ulong t;
-	int i;
+	top = off + count;
+	if(top > MAXFSIZE){
+		respond(r, Ebadoff);
+		return;
+	}
 
-	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 */
+	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;
 	}
-	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;
+
+	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;
 			}
-			if(fram->busy == 0){
-				err = Enotexist;
-				break;
+			link = &((Ram*)f->aux)->ent[i];
+			if(*link == nil){
+				memset(x, 0, sizeof(Ram));
+				x->type = Tdat;
 			}
-			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(o > x->size)
+				memset((char*)&x[1] + x->size, 0, o - x->size);
+			x->size = o + n;
+			x->link = link;
+			*link = x;
 		}
-		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;
-	}
-	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;
+		memmove((char*)&x[1] + o, p, n);
+		p += n;
+		off += n;
+		count -= n;
 	}
-	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;
-	}
-	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++;
-	}
-	rhdr.qid = r->qid;
-	rhdr.iounit = messagesize-IOHDRSZ;
-	f->open = 1;
-	r->open++;
-	return 0;
-}
 
-char *
-rcreate(Fid *f)
-{
-	Ram *r;
-	char *name;
-	long parent, prm;
+	if(top > f->length)
+		f->length = top;
+	accessfile(f, AWRITE);
 
-	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->ifcall.data;
+	respond(r, nil);
 }
 
-char*
-rread(Fid *f)
+void
+truncfile(File *f, vlong l)
 {
-	Ram *r;
-	uchar *buf;
-	vlong off;
-	int n, m, cnt;
+	int i, o, n;
+	Ram *x;
 
-	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;
+	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++;
+			}
 		}
-		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;
+		if(l == 0){
+			poolfree(&rampool, (Ram*)f->aux);
+			f->aux = nil;
 		}
-		rhdr.data = (char*)rdata;
-		rhdr.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;
-	rhdr.data = r->data+off;
-	rhdr.count = n;
-	return 0;
+	f->length = l;
 }
 
-char*
-rwrite(Fid *f)
+void
+fswstat(Req *r)
 {
-	Ram *r;
-	vlong off;
-	int cnt;
+	File *f, *w;
+	char *u;
 
-	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;
-}
+	f = r->fid->file;
+	u = r->fid->uid;
 
-static int
-emptydir(Ram *dr)
-{
-	long didx = dr - ram;
-	Ram *r;
-
-	for(r=ram; r<&ram[nram]; r++)
-		if(r->busy && didx==r->parent)
-			return 0;
-	return 1;
-}
-
-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;
-}
-
-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;
-}
-
-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);
-}
-
-char *
-rstat(Fid *f)
-{
-	if(f->ram->busy == 0)
-		return Enotexist;
-	rhdr.nstat = ramstat(f->ram, statbuf, sizeof statbuf);
-	rhdr.stat = statbuf;
-	return 0;
-}
-
-char *
-rwstat(Fid *f)
-{
-	Ram *r, *s;
-	Dir dir;
-
-	if(f->ram->busy == 0)
-		return Enotexist;
-	convM2D(thdr.stat, thdr.nstat, &dir, (char*)statbuf);
-	r = f->ram;
-
 	/*
 	 * 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) || (r->d.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;
+		}
 	}
 
 	/*
@@ -705,10 +311,12 @@
 	 * 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;
+		}
 	}
 
 	/*
@@ -716,208 +324,198 @@
 	 * 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 & ~DMDIR) | (f->mode & DMDIR);
+		f->qid.type = 0;
+		if(f->mode & DMDIR)
+			f->qid.type |= QTDIR;
+		if(f->mode & DMAPPEND)
+			f->qid.type |= QTAPPEND;
+		if(f->mode & DMEXCL)
+			f->qid.type |= QTEXCL;
 	}
-	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;
-	}
-	ram[r->parent].mtime = time(0);
-	return 0;
-}
+	if(r->d.length != ~0 && r->d.length != f->length)
+		truncfile(f, r->d.length);
 
-uint
-ramstat(Ram *r, uchar *buf, uint nbuf)
-{
-	int n;
-	Dir dir;
+	accessfile(f, AWRITE);
+	respond(r, nil);
+	return;
 
-	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;
+Perm:
+	respond(r, Eperm);
 }
 
-Fid *
-newfid(int fid)
+void
+fscreate(Req *r)
 {
-	Fid *f, *ff;
+	File *f;
+	int p;
 
-	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;
+	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;
+	.start=		fsstart,
+};
 
-	n = strlen(q)+1;
-	p = malloc(n);
-	if(!p)
-		error("out of memory");
-	memmove(p, q, n);
-	return p;
-}
-
 void
 usage(void)
 {
 	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;
+	Qid q;
+
+	fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
+	q = fs.tree->root->qid;
+
+	mountflags = 0;
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 's':
+		srvname = "/srv/ramfs";
+		mtpt = nil;
+		break;
+	case 'S':
+		srvname = EARGF(usage());
+		mtpt = nil;
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 'i':
+		fs.nopipe = 1;
+		srvname = nil;
+		mtpt = nil;
+		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(fs.nopipe){
+		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);
 }