forsyth20121205-174063a80cc13 years agoHistory
648 lines · 14.4 KB · Brainfuck
RawDownload
implement MemFS; include "sys.m";	sys: Sys;	OTRUNC, ORCLOSE, OREAD, OWRITE: import Sys;include "styx.m";	styx: Styx;	Tmsg, Rmsg: import styx;include "styxlib.m";	styxlib: Styxlib;	Styxserver: import styxlib;include "draw.m";include "arg.m"; MemFS: module {	init: fn(ctxt: ref Draw->Context, args: list of string);};  blksz : con 512;Efull : con "filesystem full"; Memfile : adt {	name : string;	owner : string;	qid : Sys->Qid;	perm : int;	atime : int;	mtime : int;	nopen : int;	data : array of array of byte;			# allocated in blks, no holes	length : int;	parent : cyclic ref Memfile;	# Dir entry linkage	kids : cyclic ref Memfile;	prev : cyclic ref Memfile;	next : cyclic ref Memfile;	hashnext : cyclic ref Memfile;	# Qid hash linkage}; Qidhash : adt {	buckets : array of ref Memfile;	nextqid : int;	new : fn () : ref Qidhash;	add : fn (h : self ref Qidhash, mf : ref Memfile);	remove : fn (h : self ref Qidhash, mf : ref Memfile);	lookup : fn (h : self ref Qidhash, qid : Sys->Qid) : ref Memfile;}; timefd: ref Sys->FD; init(nil: ref Draw->Context, argv: list of string){	sys = load Sys Sys->PATH;	styx = checkload(load Styx Styx->PATH, Styx->PATH);	styxlib = checkload(load Styxlib Styxlib->PATH, Styxlib->PATH);	arg := checkload(load Arg Arg->PATH, Arg->PATH); 	amode := Sys->MREPL;	maxsz := 16r7fffffff;	srv := 0;	mntpt := "/tmp"; 	arg->init(argv);	arg->setusage("memfs [-s] [-rab] [-m size] [mountpoint]");	while((opt := arg->opt()) != 0) {		case opt{		's' =>			srv = 1;		'r' =>			amode = Sys->MREPL;		'a' =>			amode = Sys->MAFTER;		'b' =>			amode = Sys->MBEFORE;		'm' =>			maxsz = int arg->earg();		* =>			arg->usage();		}	}	argv = arg->argv();	arg = nil;	if (argv != nil)		mntpt = hd argv; 	srvfd: ref Sys->FD;	mntfd: ref Sys->FD;	if (srv)		srvfd = sys->fildes(0);	else {		p := array [2] of ref Sys->FD;		if (sys->pipe(p) == -1)			error(sys->sprint("cannot create pipe: %r"));		mntfd = p[0];		srvfd = p[1];	}	styx->init();	styxlib->init(styx);	timefd = sys->open("/dev/time", sys->OREAD); 	(tc, styxsrv) := Styxserver.new(srvfd);	if (srv)		memfs(maxsz, tc, styxsrv, nil);	else {		sync := chan of int;		spawn memfs(maxsz, tc, styxsrv, sync);		<-sync;		if (sys->mount(mntfd, nil, mntpt, amode | Sys->MCREATE, nil) == -1)			error(sys->sprint("failed to mount onto %s: %r", mntpt));	}} checkload[T](x: T, p: string): T{	if(x == nil)		error(sys->sprint("cannot load %s: %r", p));	return x;} stderr(): ref Sys->FD{	return sys->fildes(2);} error(e: string){	sys->fprint(stderr(), "memfs: %s\n", e);	raise "fail:error";} freeblks: int; memfs(maxsz : int, tc : chan of ref Tmsg, srv : ref Styxserver, sync: chan of int){	sys->pctl(Sys->NEWNS, nil);	if (sync != nil)		sync <-= 1;	freeblks = (maxsz / blksz);	qhash := Qidhash.new(); 	# init root	root := newmf(qhash, nil, "memfs", srv.uname, 8r755 | Sys->DMDIR);	root.parent = root; 	while((tmsg := <-tc) != nil) {#		sys->print("%s\n", tmsg.text());	Msg:		pick tm := tmsg {		Readerror =>			break;		Version =>			srv.devversion(tm);		Auth =>			srv.devauth(tm);		Flush =>			srv.reply(ref Rmsg.Flush(tm.tag));		Walk =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			if (err != "") {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			nc: ref styxlib->Chan;			if (tm.newfid != tm.fid) {				nc = srv.clone(c, tm.newfid);				if (nc == nil) {					srv.reply(ref Rmsg.Error(tm.tag, "fid in use"));					continue;				}				c = nc;			}			qids: array of Sys->Qid;			if (len tm.names > 0) {				oqid := c.qid;				opath := c.path;				qids = array[len tm.names] of Sys->Qid;				wmf := mf;				for (i := 0; i < len tm.names; i++) {					wmf = dirlookup(wmf, tm.names[i]);					if (wmf == nil) {						if (nc == nil) {							c.qid = oqid;							c.path = opath;						} else							srv.chanfree(nc);						if (i == 0)							srv.reply(ref Rmsg.Error(tm.tag, Styxlib->Enotfound));						else							srv.reply(ref Rmsg.Walk(tm.tag, qids[0:i]));						break Msg;					}					c.qid = wmf.qid;					qids[i] = wmf.qid;				}			}			srv.reply(ref Rmsg.Walk(tm.tag, qids));		Open =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			if (err == "" && c.open)				err = Styxlib->Eopen;			if (err == "" && !modeok(tm.mode, mf.perm, c.uname, mf.owner))				err = Styxlib->Eperm;			if (err == "" && (mf.perm & Sys->DMDIR) && (tm.mode & (OTRUNC|OWRITE|ORCLOSE)))				err = Styxlib->Eperm;			if (err == "" && (tm.mode & ORCLOSE)) {				p := mf.parent;				if (p == nil || !modeok(OWRITE, p.perm, c.uname, p.owner))					err = Styxlib->Eperm;			} 			if (err != "") {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			} 			c.open = 1;			c.mode = tm.mode;			c.qid.vers = mf.qid.vers;			mf.nopen++;			if ((tm.mode & OTRUNC) && !(mf.perm & Sys->DMAPPEND)) {				# OTRUNC cannot be set for a directory				# always at least one blk so don't need to check fs limit				freeblks += (len mf.data);				mf.data = nil;				freeblks--;				mf.data = array[1] of {* => array [blksz] of byte};				mf.length = 0;				mf.mtime = now();			}			srv.reply(ref Rmsg.Open(tm.tag, mf.qid, Styx->MAXFDATA));		Create =>			(err, c, parent) := fidtomf(srv, qhash, tm.fid);			if (err == "" && c.open)				err = Styxlib->Eopen;			if (err == "" && !(parent.qid.qtype & Sys->QTDIR))				err = Styxlib->Enotdir;			if (err == "" && !modeok(OWRITE, parent.perm, c.uname, parent.owner))				err = Styxlib->Eperm;			if (err == "" && (tm.perm & Sys->DMDIR) && (tm.mode & (OTRUNC|OWRITE|ORCLOSE)))				err = Styxlib->Eperm;			if (err == "" && dirlookup(parent, tm.name) != nil)				err = Styxlib->Eexists; 			if (err != "") {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			} 			isdir := tm.perm & Sys->DMDIR;			if (!isdir && freeblks <= 0) {				srv.reply(ref Rmsg.Error(tm.tag, Efull));				continue;			} 			# modify perms as per Styx specification...			perm : int;			if (isdir)				perm = (tm.perm&~8r777) | (parent.perm&tm.perm&8r777);			else				perm = (tm.perm&(~8r777|8r111)) | (parent.perm&tm.perm& 8r666); 			nmf := newmf(qhash, parent, tm.name, c.uname, perm);			if (!isdir) {				freeblks--;				nmf.data = array[1] of {* => array [blksz] of byte};			} 			# link in the new MemFile			nmf.next = parent.kids;			if (parent.kids != nil)				parent.kids.prev = nmf;			parent.kids = nmf; 			c.open = 1;			c.mode = tm.mode;			c.qid = nmf.qid;			nmf.nopen = 1;			srv.reply(ref Rmsg.Create(tm.tag, nmf.qid, Styx->MAXFDATA));		Read =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			if (err == "" && !c.open)				err = Styxlib->Ebadfid; 			if (err != "") {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			data: array of byte = nil;			if (mf.perm & Sys->DMDIR)				data = dirdata(mf, int tm.offset, tm.count);			else				data = filedata(mf, int tm.offset, tm.count);			mf.atime = now();			srv.reply(ref Rmsg.Read(tm.tag, data));		Write =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			if (c != nil && !c.open)				err = Styxlib->Ebadfid;			if (err == nil && (mf.perm & Sys->DMDIR))				err = Styxlib->Eperm;			if (err == nil)				err = writefile(mf, int tm.offset, tm.data);			if (err != nil) {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			srv.reply(ref Rmsg.Write(tm.tag, len tm.data));		Clunk =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			if (c != nil)				srv.chanfree(c);			if (err != nil) {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			if (c.open) {				if (c.mode & ORCLOSE)					unlink(mf);				mf.nopen--;				freeblks += delfile(qhash, mf);			}			srv.reply(ref Rmsg.Clunk(tm.tag));		Stat =>			(err, nil, mf) := fidtomf(srv, qhash, tm.fid);			if (err != nil) {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			srv.reply(ref Rmsg.Stat(tm.tag, fileinfo(mf)));		Remove =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			if (err != nil) {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			srv.chanfree(c);			parent := mf.parent;			if (!modeok(OWRITE, parent.perm, c.uname, parent.owner))				err = Styxlib->Eperm;			if (err == "" && (mf.perm & Sys->DMDIR) && mf.kids != nil)				err = "directory not empty";			if (err == "" && mf == root)				err = "root directory";			if (err != nil) {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			} 			unlink(mf);			if (c.open)				mf.nopen--;			freeblks += delfile(qhash, mf);			srv.reply(ref Rmsg.Remove(tm.tag));		Wstat =>			(err, c, mf) := fidtomf(srv, qhash, tm.fid);			stat := tm.stat; 			if (err == nil && stat.name != mf.name) {				parent := mf.parent;				if (!modeok(OWRITE, parent.perm, c.uname, parent.owner))					err = Styxlib->Eperm;				else if (dirlookup(parent, stat.name) != nil)					err = Styxlib->Eexists;			}			if (err == nil && (stat.mode != mf.perm || stat.mtime != mf.mtime)) {				if (c.uname != mf.owner)					err = Styxlib->Eperm;			}			if (err != nil) {				srv.reply(ref Rmsg.Error(tm.tag, err));				continue;			}			isdir := mf.perm & Sys->DMDIR;			if(stat.name != nil)				mf.name = stat.name;			if(stat.mode != ~0)				mf.perm = stat.mode | isdir;			if(stat.mtime != ~0)				mf.mtime = stat.mtime; 			if(stat.uid != nil) 				mf.owner = stat.uid;			t := now();			mf.atime = t;			mf.parent.mtime = t;			# not supporting group id at the moment			srv.reply(ref Rmsg.Wstat(tm.tag));		Attach =>			c := srv.newchan(tm.fid);			if (c == nil) {				srv.reply(ref Rmsg.Error(tm.tag, Styxlib->Einuse));				continue;			}			c.uname = tm.uname;			c.qid = root.qid;			srv.reply(ref Rmsg.Attach(tm.tag, c.qid));		}	}} writefile(mf: ref Memfile, offset: int, data: array of byte): string{	if(mf.perm & Sys->DMAPPEND)		offset = mf.length;	startblk := offset/blksz;	nblks := ((len data + offset) - (startblk * blksz))/blksz;	lastblk := startblk + nblks;	need := lastblk + 1 - len mf.data;	if (need > 0) {		if (need > freeblks)			return Efull;		mf.data = (array [lastblk+1] of array of byte)[:] = mf.data;		freeblks -= need;	}	mf.length = max(mf.length, offset + len data); 	# handle (possibly incomplete first block) separately	offset %= blksz;	end := min(blksz-offset, len data);	if (mf.data[startblk] == nil)		mf.data[startblk] = array [blksz] of byte;	mf.data[startblk++][offset:] = data[:end]; 	ix := blksz - offset;	while (ix < len data) {		if (mf.data[startblk] == nil)			mf.data[startblk] = array [blksz] of byte;		end = min(ix+blksz,len data);		mf.data[startblk++][:] = data[ix:end];		ix += blksz;	}	mf.mtime = now();	return nil;} filedata(mf: ref Memfile, offset, n: int): array of byte{	if (offset +n > mf.length)		n = mf.length - offset;	if (n == 0)		return nil; 	data := array [n] of byte;	startblk := offset/blksz;	offset %= blksz;	rn := min(blksz - offset, n);	data[:] = mf.data[startblk++][offset:offset+rn];	ix := blksz - offset;	while (ix < n) {		rn = blksz;		if (ix+rn > n)			rn = n - ix;		data[ix:] = mf.data[startblk++][:rn];		ix += blksz;	}	return data;} QHSIZE: con 256;QHMASK: con QHSIZE-1; Qidhash.new() : ref Qidhash{	qh := ref Qidhash;	qh.buckets = array [QHSIZE] of ref Memfile;	qh.nextqid = 0;	return qh;} Qidhash.add(h : self ref Qidhash, mf : ref Memfile){	path := h.nextqid++;	mf.qid = Sys->Qid(big path, 0, Sys->QTFILE);	bix := path & QHMASK;	mf.hashnext = h.buckets[bix];	h.buckets[bix] = mf;} Qidhash.remove(h : self ref Qidhash, mf : ref Memfile){ 	bix := int mf.qid.path & QHMASK;	prev : ref Memfile;	for (cur := h.buckets[bix]; cur != nil; cur = cur.hashnext) {		if (cur == mf)			break;		prev = cur;	}	if (cur != nil) {		if (prev != nil)			prev.hashnext = cur.hashnext;		else			h.buckets[bix] = cur.hashnext;		cur.hashnext = nil;	}} Qidhash.lookup(h : self ref Qidhash, qid : Sys->Qid) : ref Memfile{	bix := int qid.path & QHMASK;	for (mf := h.buckets[bix]; mf != nil; mf = mf.hashnext)

Lines 1–500 of 648.