Charles.Forsyth20060303a828770d19 years agoHistory
773 lines · 17.1 KB · Brainfuck
RawDownload
implement Lockfs;include "sys.m";	sys: Sys;	stderr: ref Sys->FD;include "draw.m";include "styx.m";	styx: Styx;	Tmsg, Rmsg: import styx;include "styxlib.m";	styxlib: Styxlib;	Dirtab, Styxserver, Chan,	devdir,	Eperm, Ebadfid, Eexists, Enotdir, Enotfound, Einuse: import styxlib;include "arg.m";include "keyring.m";	keyring: Keyring;include "security.m";	auth: Auth; Lockfs: module {	init: fn(nil: ref Draw->Context, argv: list of string);	dirgen: fn(srv: ref Styxlib->Styxserver, c: ref Styxlib->Chan,			tab: array of Styxlib->Dirtab, i: int): (int, Sys->Dir);}; Elocked: con "file is locked"; devgen: Dirgenmod; Openreq: adt {	srv: ref Styxserver;	tag: int;	omode: int;	c: ref Chan;	uproc: Uproc;}; Lockqueue: adt {	h: list of ref Openreq; 	t: list of ref Openreq;	put: fn(q: self ref Lockqueue, s: ref Openreq);	get: fn(q: self ref Lockqueue): ref Openreq;	peek: fn(q: self ref Lockqueue): ref Openreq;	flush: fn(q: self ref Lockqueue, srv: ref Styxserver, tag: int);}; Lockfile: adt {	waitq: ref Lockqueue;	fd: ref Sys->FD;	readers: int;	writers: int;	d: Sys->Dir;}; Ureq: adt {	fname: string;	pick {	Open =>		omode: int;	Create =>		omode: int;		perm: int;	Remove =>	Wstat =>		dir: Sys->Dir;	}}; Uproc: type chan of (ref Ureq, chan of (ref Sys->FD, string)); maxqidpath := big 1;locks: list of ref Lockfile;lockdir: string;authinfo: ref Keyring->Authinfo;timefd: ref Sys->FD; MAXCONN: con 20; verbose := 0; usage(){	sys->fprint(stderr, "usage: lockfs [-A] [-a alg]... [-p addr] dir [mountpoint]\n");	raise "fail:usage";} badmodule(p: string){	sys->fprint(stderr, "lockfs: cannot load %s: %r\n", p);	raise "fail:bad module";} init(nil: ref Draw->Context, argv: list of string){	sys = load Sys Sys->PATH;	stderr = sys->fildes(2);	styx = load Styx Styx->PATH;	if (styx == nil)		badmodule(Styx->PATH);	styx->init();	styxlib = load Styxlib Styxlib->PATH;	if (styxlib == nil)		badmodule(Styxlib->PATH);	styxlib->init(styx);	devgen = load Dirgenmod "$self";	if (devgen == nil)		badmodule("self as Dirgenmod");	timefd = sys->open("/dev/time", sys->OREAD);	if (timefd == nil) {		sys->fprint(stderr, "lockfs: cannot open /dev/time: %r\n");		raise "fail:no time";	}	arg := load Arg Arg->PATH;	if (arg == nil)		badmodule(Arg->PATH);	arg->init(argv); 	addr := "";	doauth := 1;	algs: list of string;	while ((opt := arg->opt()) != 0) {		case opt {		'p' =>			addr = arg->arg();		'a' =>			alg := arg->arg();			if (alg == nil)				usage();			algs = alg :: algs;		'A' =>			doauth = 0;		'v' =>			verbose = 1;		* =>			usage();		}	}	argv = arg->argv();	if (argv == nil || (addr != nil && tl argv != nil))		usage();	if (addr == nil)		doauth = 0;		# no authentication necessary for local mount	if (doauth) {		auth = load Auth Auth->PATH;		if (auth == nil)			badmodule(Auth->PATH);		if ((e := auth->init()) != nil) {			sys->fprint(stderr, "lockfs: cannot init auth: %s\n", e);			raise "fail:errors";		}		keyring = load Keyring Keyring->PATH;		if (keyring == nil)			badmodule(Keyring->PATH);		authinfo = keyring->readauthinfo("/usr/" + user() + "/keyring/default");	} 	mountpoint := lockdir = hd argv;	if (tl argv != nil)		mountpoint = hd tl argv;	if (addr != nil) {		if (doauth && algs == nil)			algs = "none" :: nil;		# XXX is this default a bad idea?		srvrq := chan of (ref Sys->FD, string, Uproc);		srvsync := chan of (int, string);		spawn listener(addr, srvrq, srvsync, algs);		(srvpid, err) := <-srvsync;		srvsync = nil;		if (srvpid == -1) {			sys->fprint(stderr, "lockfs: failed to start listener: %s\n", err);			raise "fail:errors";		}		sync := chan of int;		spawn server(srvrq, sync);		<-sync;	} else {		rq := chan of (ref Sys->FD, string, Uproc);		fds := array[2] of ref Sys->FD;		sys->pipe(fds);		sync := chan of int;		spawn server(rq, sync);		<-sync;		rq <-= (fds[0], "lock", nil);		rq <-= (nil, nil, nil);		if (sys->mount(fds[1], nil, mountpoint, Sys->MREPL | Sys->MCREATE, nil) == -1) {			sys->fprint(stderr, "lockfs: cannot mount: %r\n");			raise "fail:cannot mount";		}	}} server(srvrq: chan of (ref Sys->FD, string, Uproc), sync: chan of int){	sys->pctl(Sys->FORKNS, nil);	sync <-= 1;	down := 0;	nclient := 0;	tchans := array[MAXCONN] of chan of ref Tmsg;	srv := array[MAXCONN] of ref Styxserver;	uprocs := array[MAXCONN] of Uproc;	lockinit();Service:	for (;;) alt {	(fd, reqstr, uprocch) := <-srvrq =>		if (fd == nil) {			if (verbose && reqstr != nil)				sys->print("lockfs: localserver going down (reason: %s)\n", reqstr);			down = 1;		} else {			if (verbose)				sys->print("lockfs: got new connection (s == '%s')\n", reqstr);			for (i := 0; i < len tchans; i++)				if (tchans[i] == nil) {					(tchans[i], srv[i]) = Styxserver.new(fd);					if(verbose)						sys->print("svc started\n");					uprocs[i] = uprocch;					break;				}			if (i == len tchans) {				sys->fprint(stderr, "lockfs: too many clients\n");	# XXX expand arrays				if (uprocch != nil)					uprocch <-= (nil, nil);			} else				nclient++;		}	(n, gm) := <-tchans =>		if (handletmsg(srv[n], gm, uprocs[n]) == -1) {			tchans[n] = nil;			srv[n] = nil;			if (uprocs[n] != nil) {				uprocs[n] <-= (nil, nil);				uprocs[n] = nil;			}			if (nclient-- <= 1 && down)				break Service;		}	}	if (verbose)		sys->print("lockfs: finished\n");} dirgen(nil: ref Styxserver, nil: ref Styxlib->Chan,				nil: array of Dirtab, s: int): (int, Sys->Dir){	d: Sys->Dir;	ll := locks;	for (i := 0; i < s && ll != nil; i++)		ll = tl ll;	if (ll == nil)		return (-1, d);	return (1, (hd ll).d);}		handletmsg(srv:  ref Styxserver, gm: ref Tmsg, uproc: Uproc): int{{	if (gm == nil)		gm = ref Tmsg.Readerror(-1, "eof");	if(verbose)		sys->print("<- %s\n", gm.text());	pick m := gm {	Readerror =>		# could be more efficient...		for (cl := srv.chanlist(); cl != nil; cl = tl cl) {			c := hd cl;			for (ll := locks; ll != nil; ll = tl ll) {				if ((hd ll).d.qid.path == c.qid.path) {					l := hd ll;					l.waitq.flush(srv, -1);					if (c.open)						unlocked(l);					break;				}			}		}		if (m.error != "eof")			sys->fprint(stderr, "lockfs: read error: %s\n", m.error);		return -1;	Version =>		srv.devversion(m);	Auth =>		srv.devauth(m);	Walk =>		c := fid2chan(srv, m.fid);		qids: array of Sys->Qid;		cc := ref *c;		if (len m.names > 0) {			qids = array[1] of Sys->Qid;	# it's just one level			if ((cc.qid.qtype & Sys->QTDIR) == 0) {				srv.reply(ref Rmsg.Error(m.tag, Enotdir));				break;			}			for (ll := locks; ll != nil; ll = tl ll)				if ((hd ll).d.name == m.names[0])					break;			if (ll == nil) {				srv.reply(ref Rmsg.Error(m.tag, Enotfound));				break;			}			d := (hd ll).d;			cc.qid = d.qid;			cc.path = d.name;			qids[0] = c.qid;		}		if(m.newfid != m.fid){			nc := srv.clone(cc, m.newfid);			if(nc == nil){				srv.reply(ref Rmsg.Error(m.tag, Einuse));				break;			}		}else{			c.qid = cc.qid;			c.path = cc.path;		}		srv.reply(ref Rmsg.Walk(m.tag, qids));	Open =>		c := fid2chan(srv, m.fid);		if (c.qid.qtype & Sys->QTDIR) {			srv.reply(ref Rmsg.Open(m.tag, c.qid, Styx->MAXFDATA));			break;		}		for (ll := locks; ll != nil; ll = tl ll)			if ((hd ll).d.qid.path == c.qid.path)				break;		if (ll == nil) {			srv.reply(ref Rmsg.Error(m.tag, Enotfound));			break;		}		l := hd ll;		req := ref Openreq(srv, m.tag, m.mode, c, uproc);		if (l.fd == nil || (m.mode == Sys->OREAD && l.writers == 0)) {			openlockfile(l, req);		} else {			l.waitq.put(req);		}		req = nil;	Create =>		c := fid2chan(srv, m.fid);		if ((c.qid.qtype & Sys->QTDIR) == 0) {			srv.reply(ref Rmsg.Error(m.tag, Enotdir));			break;		}		if (m.perm & Sys->DMDIR) {			srv.reply(ref Rmsg.Error(m.tag, Eperm));			break;		}		for (ll := locks; ll != nil; ll = tl ll)			if ((hd ll).d.name == m.name)				break;		if (ll != nil) {			srv.reply(ref Rmsg.Error(m.tag, Eexists));			break;		}		(fd, err) := create(uproc, lockdir + "/" + m.name, m.mode, m.perm);		if (fd == nil) {			srv.reply(ref Rmsg.Error(m.tag, err));			break;		}		(ok, d) := sys->fstat(fd);		if (ok == -1) {			srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r")));			break;		}		l := ref Lockfile(ref Lockqueue, fd, 0, 0, d);		l.d.qid = (maxqidpath++, 0, Sys->QTFILE);		l.d.mtime = l.d.atime = now();		if (m.mode == Sys->OREAD)			l.readers = 1;		else			l.writers = 1;		locks = l :: locks;		c.qid.path = (hd locks).d.qid.path;		c.open = 1;		srv.reply(ref Rmsg.Create(m.tag, c.qid, Styx->MAXFDATA));	Read =>		c := fid2chan(srv, m.fid);		if (c.qid.qtype & Sys->QTDIR)			srv.devdirread(m, devgen, nil);		else {			l := qid2lock(c.qid);			if (l == nil)				srv.reply(ref Rmsg.Error(m.tag, Enotfound));			else {				d := array[m.count] of byte;				sys->seek(l.fd, m.offset, Sys->SEEKSTART);				n := sys->read(l.fd, d, m.count);				if (n == -1)					srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r")));				else {					srv.reply(ref Rmsg.Read(m.tag, d[0:n]));					l.d.atime = now();				}			}		}	Write =>		c := fid2chan(srv, m.fid);		if (c.qid.qtype & Sys->QTDIR) {			srv.reply(ref Rmsg.Error(m.tag, Eperm));			break;		}		l := qid2lock(c.qid);		if (l == nil) {			srv.reply(ref Rmsg.Error(m.tag, Enotfound));			break;		}		sys->seek(l.fd, m.offset, Sys->SEEKSTART);		n := sys->write(l.fd, m.data, len m.data);		if (n == -1)			srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r")));		else {			srv.reply(ref Rmsg.Write(m.tag, n));			nlength := m.offset + big n;			if (nlength > l.d.length)				l.d.length = nlength;			l.d.mtime = now();			l.d.qid.vers++;		}	Clunk =>		c := srv.devclunk(m);		if (c != nil && c.open && (l := qid2lock(c.qid)) != nil)			unlocked(l);	Flush =>		for (ll := locks; ll != nil; ll = tl ll)			(hd ll).waitq.flush(srv, m.tag);		srv.reply(ref Rmsg.Flush(m.tag));	Stat =>		srv.devstat(m, devgen, nil);	Remove =>		c := fid2chan(srv, m.fid);		srv.chanfree(c);		if (c.qid.qtype & Sys->QTDIR) {			srv.reply(ref Rmsg.Error(m.tag, Eperm));			break;		}		l := qid2lock(c.qid);		if (l == nil) {			srv.reply(ref Rmsg.Error(m.tag, Enotfound));			break;		}		if (l.fd != nil) {			srv.reply(ref Rmsg.Error(m.tag, Elocked));			break;		}		if ((err := remove(uproc, lockdir + "/" + l.d.name)) == nil) {			srv.reply(ref Rmsg.Error(m.tag, err));			break;		}		ll: list of ref Lockfile;		for (; locks != nil; locks = tl locks)			if (hd locks != l)				ll = hd locks :: ll;		locks = ll;		srv.reply(ref Rmsg.Remove(m.tag));	Wstat =>		c := fid2chan(srv, m.fid);		if (c.qid.qtype & Sys->QTDIR) {			srv.reply(ref Rmsg.Error(m.tag, Eperm));			break;		}		l := qid2lock(c.qid);		if (l == nil) {			srv.reply(ref Rmsg.Error(m.tag, Enotfound));			break;		}		if ((err := wstat(uproc, lockdir + "/" + l.d.name, m.stat)) != nil) {			srv.reply(ref Rmsg.Error(m.tag, err));			break;		}		(ok, d) := sys->stat(lockdir + "/" + m.stat.name);		if (ok == -1) {			srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r")));			break;		}		d.qid = l.d.qid;		l.d = d;		srv.reply(ref Rmsg.Wstat(m.tag));	Attach =>		srv.devattach(m);	}	return 0;}exception e{	"panic:*" =>		sys->fprint(stderr, "lockfs: %s\n", e);		srv.reply(ref Rmsg.Error(gm.tag, e[len "panic:":]));		return 0;}} unlocked(l: ref Lockfile){	if (l.readers > 0)		l.readers--;	else		l.writers--;	if (l.readers > 0)		return;	l.fd = nil; 	# unblock all readers at the head of the queue.

Lines 1–500 of 773.