Charles.Forsyth20060303a828770d19 years agoHistory
813 lines · 18.1 KB · Brainfuck
RawDownload
implement Dbfs; ## Copyright © 1999, 2002 Vita Nuova Limited.  All rights reserved.# # Enhanced to include record locking, index field generation and update notification # TO DO:#	make writing & reading more like real files; don't ignore offsets.#	open with OTRUNC should work.#	provide some way of compacting a dbfs file. include "sys.m";	sys: Sys;	Qid: import Sys; include "draw.m"; include "arg.m"; include "styx.m";	styx: Styx;	Rmsg, Tmsg: import styx; include "styxservers.m";	styxservers: Styxservers;	Styxserver, Fid, Navigator, Navop: import styxservers;	Enotfound, Eperm, Ebadfid, Ebadarg: import styxservers; include "string.m";	str: String; include "bufio.m";	bufio: Bufio;	Iobuf: import bufio; include "sh.m";	sh: Sh; Record: adt {	id:		int;			# file number in directory (if block is allocated)	offset:	int;			# start of data	count:	int;			# length of block (excluding header)	datalen:	int;			# length of data (-1 if block is free)	vers:		int;			# version 	new:		fn(offset: int, length: int): ref Record;	qid:		fn(r: self ref Record): Sys->Qid;}; # Record lockLock: adt {	qpath: big;	fid:	int;}; HEADLEN: con 10;MINSIZE: con 20; Database: adt {	file:		ref Iobuf;	records:	array of ref Record;	maxid:	int;	locking:	int;	locklist:	list of Lock;	indexing:	int;	stats:	int;	index:	int;	s_reads:	int;	s_writes:	int;	s_creates:	int;	s_removes:	int;	updcmd:	string;	vers:		int; 	build:	fn(f: ref Iobuf, locking, indexing: int, stats: int, updcmd: string): (ref Database, string);	write:	fn(db: self ref Database, n: int, data: array of byte): int;	read:		fn(db: self ref Database, n: int): array of byte;	remove:	fn(db: self ref Database, n: int);	create:	fn(db: self ref Database, data: array of byte): ref Record;	updated:	fn(db: self ref Database);	lock:		fn(db: self ref Database, c: ref Styxservers->Fid): int;	unlock:	fn(db: self ref Database, c: ref Styxservers->Fid);	ownlock:	fn(db: self ref Database, c: ref Styxservers->Fid): int;}; Dbfs: module{	init:	fn(ctxt: ref Draw->Context, nil: list of string);}; Qdir, Qnew, Qdata, Qindex, Qstats: con iota; stderr: ref Sys->FD;database: ref Database;context: ref Draw->Context;user: string;Eremoved: con "file removed";Egreg: con "thermal problems";Elocked: con "open/create -- file is locked"; usage(){	sys->fprint(stderr, "Usage: dbfs [-abcelrxD][-u cmd] file mountpoint\n");	raise "fail:usage";} nomod(s: string){	sys->fprint(stderr, "dbfs: can't load %s: %r\n", s);	raise "fail:load";} init(ctxt: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH;	stderr = sys->fildes(2);	context = ctxt;	sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);	styx = load Styx Styx->PATH;	if(styx == nil)		nomod(Styx->PATH);	styx->init();	styxservers = load Styxservers Styxservers->PATH;	if(styxservers == nil)		nomod(Styxservers->PATH);	styxservers->init(styx);	str = load String String->PATH;	if(str == nil)		nomod(String->PATH);	bufio = load Bufio Bufio->PATH;	if(bufio == nil)		nomod(Bufio->PATH); 	arg := load Arg Arg->PATH;	if(arg == nil)		nomod(Arg->PATH);	arg->init(args);	flags := Sys->MREPL;	copt := 0;	empty := 0;	locking := 0;	stats := 0;	indexing := 0;	updcmd := "";	while((o := arg->opt()) != 0)		case o {		'a' =>	flags = Sys->MAFTER;		'b' =>	flags = Sys->MBEFORE;		'r' =>		flags = Sys->MREPL;		'c' =>	copt = 1;		'e' =>	empty = 1;		'l' =>		locking = 1;		'u' =>	updcmd = arg->arg();				if(updcmd == nil)					usage();		'x' =>	indexing = 1;				stats = 1;		'D' =>	styxservers->traceset(1);		* =>		usage();		}	args = arg->argv();	arg = nil; 	if(len args != 2)		usage();	if(copt)		flags |= Sys->MCREATE;	file := hd args;	args = tl args;	mountpt := hd args; 	if(updcmd != nil){		sh = load Sh Sh->PATH;		if(sh == nil)			nomod(Sh->PATH);	} 	df := bufio->open(file, Sys->ORDWR);	if(df == nil && empty){		(rc, d) := sys->stat(file);		if(rc < 0)			df = bufio->create(file, Sys->ORDWR, 8r600);	}	if(df == nil){		sys->fprint(stderr, "dbfs: can't open %s: %r\n", file);		raise "fail:cannot open file";	}	(db, err) := Database.build(df, locking, indexing, stats, updcmd);	if(db == nil){		sys->fprint(stderr, "dbfs: can't read %s: %s\n", file, err);		raise "fail:cannot read db";	}	database = db; 	sys->pctl(Sys->FORKFD, nil); 	user = rf("/dev/user");	if(user == nil)		user = "inferno"; 	fds := array[2] of ref Sys->FD;	if(sys->pipe(fds) < 0){		sys->fprint(stderr, "dbfs: can't create pipe: %r\n");		raise "fail:pipe";	} 	navops := chan of ref Navop;	spawn navigator(navops); 	(tchan, srv) := Styxserver.new(fds[0], Navigator.new(navops), big Qdir);	fds[0] = nil; 	pidc := chan of int;	spawn serveloop(tchan, srv, pidc, navops);	<-pidc; 	if(sys->mount(fds[1], nil, mountpt, flags, nil) < 0) {		sys->fprint(stderr, "dbfs: mount failed: %r\n");		raise "fail:bad mount";	}} rf(f: string): string{	fd := sys->open(f, Sys->OREAD);	if(fd == nil)		return nil;	b := array[Sys->NAMEMAX] of byte;	n := sys->read(fd, b, len b);	if(n < 0)		return nil;	return string b[0:n];} serveloop(tchan: chan of ref Tmsg, srv: ref Styxserver, pidc: chan of int, navops: chan of ref Navop){	pidc <-= sys->pctl(Sys->FORKNS|Sys->NEWFD, stderr.fd::1::2::database.file.fd.fd::srv.fd.fd::nil);#	stderr = sys->fildes(stderr.fd);	database.file.fd = sys->fildes(database.file.fd.fd);Serve:	while((gm := <-tchan) != nil){		pick m := gm {		Readerror =>			sys->fprint(stderr, "dbfs: fatal read error: %s\n", m.error);			break Serve;		Open =>			c := srv.getfid(m.fid);			open(srv, m);		Read =>			(c, err) := srv.canread(m);			if(c == nil) {				srv.reply(ref Rmsg.Error(m.tag, err));				break;			}			if(c.qtype & Sys->QTDIR){				srv.read(m);				break;			}			case TYPE(c.path) {			Qindex =>				if(database.index < 0) {					srv.reply(ref Rmsg.Error(m.tag, Eperm));					break;				}				if (m.offset > big 0) {					srv.reply(ref Rmsg.Read(m.tag, nil));					break;				}				reply := array of byte string ++database.index;				if(m.count < len reply)					reply = reply[:m.count];				srv.reply(ref Rmsg.Read(m.tag, reply));			Qstats =>				if (m.offset > big 0) {					srv.reply(ref Rmsg.Read(m.tag, nil));					break;				}				reply := array of byte sys->sprint("%d %d %d %d", database.s_reads, database.s_writes,												database.s_creates, database.s_removes);				if(m.count < len reply) reply = reply[:m.count];				srv.reply(ref Rmsg.Read(m.tag, reply));			Qdata =>				recno := id2recno(FILENO(c.path));				if(recno == -1)					srv.reply(ref Rmsg.Error(m.tag, Eremoved));				else					srv.reply(styxservers->readbytes(m, database.read(recno)));			* =>				srv.reply(ref Rmsg.Error(m.tag, Egreg));			}		Write =>			(c, err) := srv.canwrite(m);			if(c == nil){				srv.reply(ref Rmsg.Error(m.tag, err));				break;			}			if(!database.ownlock(c)) {				# shouldn't happen: open checks				srv.reply(ref Rmsg.Error(m.tag, Elocked));				break;			}			case TYPE(c.path) {			Qindex =>				if(database.index >= 0) {					srv.reply(ref Rmsg.Error(m.tag, Eperm));					break;				}				database.index = int string m.data;				srv.reply(ref Rmsg.Write(m.tag, len m.data));			Qdata =>				recno := id2recno(FILENO(c.path));				if(recno == -1)					srv.reply(ref Rmsg.Error(m.tag, "phase error"));				else {					changed := 1;					if(database.updcmd != nil){						oldrec := database.read(recno);						changed = !eqbytes(m.data, oldrec);					}					if(changed && database.write(recno, m.data) == -1){						srv.reply(ref Rmsg.Error(m.tag, sys->sprint("%r")));						break;					}					if(changed)						database.updated();	# run the command before reply					srv.reply(ref Rmsg.Write(m.tag, len m.data));				}			* =>				srv.reply(ref Rmsg.Error(m.tag, Eperm));			}		Clunk =>			c := srv.getfid(m.fid);			if(c != nil)				database.unlock(c);			srv.clunk(m);		Remove =>			c := srv.getfid(m.fid);			database.unlock(c);			if(c == nil || c.qtype & Sys->QTDIR || TYPE(c.path) != Qdata){				# let it diagnose all the errors				srv.remove(m);				break;			}			recno := id2recno(FILENO(c.path));			if(recno == -1)				srv.reply(ref Rmsg.Error(m.tag, "phase error"));			else {				database.remove(recno);				database.updated();				srv.reply(ref Rmsg.Remove(m.tag));			}			srv.delfid(c);		* =>			srv.default(gm);		}	}	navops <-= nil;		# shut down navigator} eqbytes(a, b: array of byte): int{	if(len a != len b)		return 0;	for(i := 0; i < len a; i++)		if(a[i] != b[i])			return 0;	return 1;} id2recno(id: int): int{	recs := database.records;	for(i := 0; i < len recs; i++)		if(recs[i].datalen >= 0 && recs[i].id == id)			return i;	return -1;}	open(srv: ref Styxserver, m: ref Tmsg.Open): ref Fid{	(c, mode, d, err) := srv.canopen(m);	if(c == nil){		srv.reply(ref Rmsg.Error(m.tag, err));		return nil;	}	if(TYPE(c.path) == Qnew){		# generate new file		if(c.uname != user){			srv.reply(ref Rmsg.Error(m.tag, Eperm));			return nil;		}		r := database.create(array[0] of byte);		if(r == nil) {			srv.reply(ref Rmsg.Error(m.tag, "create -- i/o error"));			return nil;		}		(d, nil) = dirgen(QPATH(r.id, Qdata));	}	if(m.mode & Sys->OTRUNC) {		# TO DO	}	c.open(mode, d.qid);	if(database.locking && TYPE(c.path) == Qdata && (m.mode & (Sys->OWRITE|Sys->ORDWR))) {		if(!database.lock(c)) {			srv.reply(ref Rmsg.Error(m.tag, Elocked));			return nil;		}	}	srv.reply(ref Rmsg.Open(m.tag, d.qid, srv.iounit()));	return c;} dirslot(n: int): int{	for(i := 0; i < len database.records; i++){		r := database.records[i];		if(r != nil && r.datalen >= 0){			if(n == 0)				return i;			n--;		}	}	return -1;} dir(qid: Sys->Qid, name: string, length: big, uid: string, perm: int): ref Sys->Dir{	d := ref sys->zerodir;	d.qid = qid;	if(qid.qtype & Sys->QTDIR)		perm |= Sys->DMDIR;	d.mode = perm;	d.name = name;	d.uid = uid;	d.gid = uid;	d.length = length;	return d;} dirgen(p: big): (ref Sys->Dir, string){	case TYPE(p) {	Qdir =>		return (dir(Qid(QPATH(0, Qdir),database.vers,Sys->QTDIR), "/", big 0, user, 8r700), nil);	Qnew =>		return (dir(Qid(QPATH(0, Qnew),0,Sys->QTFILE), "new", big 0, user, 8r600), nil);	Qindex =>		return (dir(Qid(QPATH(0, Qindex),0,Sys->QTFILE), "index", big 0, user, 8r600), nil);	Qstats =>		return (dir(Qid(QPATH(0, Qstats),0,Sys->QTFILE), "stats", big 0, user, 8r400), nil);	* =>		n := id2recno(FILENO(p));		if(n < 0 || n >= len database.records)			return (nil, nil);		r := database.records[n];		if(r == nil || r.datalen < 0)			return (nil, Enotfound);		l := r.datalen;		if(l < 0)			l = 0;		return (dir(r.qid(), sys->sprint("%d", r.id), big l, user, 8r600), nil);	}} navigator(navops: chan of ref Navop){	while((m := <-navops) != nil){		pick n := m {		Stat =>			n.reply <-= dirgen(n.path);		Walk =>			if(int n.path != Qdir){				n.reply <-= (nil, "not a directory");				break;			}			case n.name {			".." =>				;	# nop			"new" =>				n.path = QPATH(0, Qnew);			"stats" =>				if(!database.indexing){					n.reply <-= (nil, Enotfound);					continue;				}				n.path = QPATH(0, Qstats);			"index" =>				if(!database.indexing){					n.reply <-= (nil, Enotfound);					continue;				}				n.path = QPATH(0, Qindex);			* =>				if(len n.name < 1 || !(n.name[0]>='0' && n.name[0]<='9')){	# weak test for now					n.reply <-= (nil, Enotfound);					continue;				}				n.path = QPATH(int n.name, Qdata);

Lines 1–500 of 813.