code: 9ferno

ref: d2ddb4edfec6500aa3e59e0517e9cc46eb146310
dir: /appl/cmd/vacfs.b/

View raw version
implement Vacfs;

include "sys.m";
	sys: Sys;
	sprint: import sys;
include "draw.m";
include "arg.m";
include "dial.m";
	dial: Dial;
include "string.m";
	str: String;
include "daytime.m";
	dt: Daytime;
include "venti.m";
	venti: Venti;
	Root, Entry, Score, Session: import venti;
	Roottype, Dirtype, Pointertype0, Datatype: import venti;
include "vac.m";
	vac: Vac;
	Direntry, Vacdir, Vacfile: import vac;
include "styx.m";
	styx: Styx;
	Tmsg, Rmsg: import styx;
include "styxservers.m";
	styxservers: Styxservers;
	Styxserver, Fid, Navigator, Navop, Enotfound: import styxservers;

Vacfs: module {
	init:	fn(nil: ref Draw->Context, args: list of string);
};

addr := "$venti";
dflag: int;
pflag: int;
session: ref Session;

ss: ref Styxserver;

Elem: adt {
	qid:	int;
	de: 	ref Direntry;
	size:	big;
	pick {
	File =>	vf: 	ref Vacfile;
	Dir =>	vd:	ref Vacdir;
		pqid:	int;
		offset:	int;
		nprev:	int;
		prev:	array of ref Sys->Dir;
	}

	new:	fn(nqid: int, vd: ref Vacdir, de: ref Direntry, pqid: int): ref Elem;
	stat:	fn(e: self ref Elem): ref Sys->Dir;
};

Qdir: adt {
	qid:	int;
	cqids:	list of (string, int); # name, qid
};

elems := array[512] of list of ref Elem;
qids := array[512] of list of ref Qdir;
lastqid := 0;
qidscores: list of (string, int);


childget(qid: int, name: string): ref Elem
{
	for(l := qids[qid % len qids]; l != nil; l = tl l) {
		if((hd l).qid != qid)
			continue;
		for(m := (hd l).cqids; m != nil; m = tl m) {
			(cname, cq) := hd m;
			if(name == cname)
				return get(cq);
		}
	}
	return nil;
}

childput(qid: int, name: string): int
{
	qd: ref Qdir;
	for(l := qids[qid % len qids]; l != nil; l = tl l)
		if((hd l).qid == qid) {
			qd = hd l;
			break;
		}
	if(qd == nil) {
		qd = ref Qdir(qid, nil);
		qids[qid % len qids] = qd::nil;
	}
	qd.cqids = (name, ++lastqid)::qd.cqids;
	return lastqid;
}

scoreget(score: string): ref Elem
{
	for(l := qidscores; l != nil; l = tl l) {
		(s, n) := hd l;
		if(s == score)
			return get(n);
	}
	return nil;
}

scoreput(score: string): int
{
	qidscores = (score, ++lastqid)::qidscores;
	return lastqid;
}


Elem.new(nqid: int, vd: ref Vacdir, de: ref Direntry, pqid: int): ref Elem
{
	(e, me) := vd.open(de);
	if(e == nil)
		return nil;
	if(de.mode & Vac->Modedir)
		return ref Elem.Dir(nqid, de, e.size, Vacdir.new(session, e, me), pqid, 0, 0, nil);
	return ref Elem.File(nqid, de, e.size, Vacfile.new(session, e));
}

Elem.stat(e: self ref Elem): ref Sys->Dir
{
	d := e.de.mkdir();
	d.qid.path = big e.qid;
	d.length = e.size;
	return d;
}

walk(ed: ref Elem.Dir, name: string): (ref Elem, string)
{
	if(name == "..")
		return (get(ed.pqid), nil);

	if(ed.qid == 0) {
		ne := scoreget(name);
		if(ne == nil) {
			(ok, score) := Score.parse(name);
			if(ok != 0)
				return (nil, "bad score: "+name);

			(vd, de, err) := vac->vdroot(session, score);
			if(err != nil)
				return (nil, err);

			nqid := scoreput(name);
			ne = ref Elem.Dir(nqid, de, big 0, vd, ed.qid, 0, 0, nil);
			set(ne);
		}
		return (ne, nil);
	}

	de := ed.vd.walk(name);
	if(de == nil)
		return (nil, sprint("%r"));
	ne := childget(ed.qid, de.elem);
	if(ne == nil) {
		nqid := childput(ed.qid, de.elem);
		ne = Elem.new(nqid, ed.vd, de, ed.qid);
		if(ne == nil)
			return (nil, sprint("%r"));
		set(ne);
	}
	return (ne, nil);
}

get(qid: int): ref Elem
{
	for(l := elems[qid % len elems]; l != nil; l = tl l)
		if((hd l).qid == qid)
			return hd l;
	return nil;
}

set(e: ref Elem)
{
	elems[e.qid % len elems] = e::elems[e.qid % len elems];
}

getfile(qid: int): ref Elem.File
{
	pick file := get(qid) {
	File =>	return file;
	}
	fail("internal error, getfile");
	return nil;
}

getdir(qid: int): ref Elem.Dir
{
	pick d := get(qid) {
	Dir =>	return d;
	}
	fail("internal error, getdir");
	return nil;
}

init(nil: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;
	arg := load Arg Arg->PATH;
	dial = load Dial Dial->PATH;
	str = load String String->PATH;
	dt = load Daytime Daytime->PATH;
	venti = load Venti Venti->PATH;
	styx = load Styx Styx->PATH;
	styxservers = load Styxservers Styxservers->PATH;
	vac = load Vac Vac->PATH;
	venti->init();
	vac->init();
	styx->init();
	styxservers->init(styx);

	sys->pctl(sys->NEWPGRP, nil);
	if(venti == nil || vac == nil)
		fail("loading venti,vac");

	arg->init(args);
	arg->setusage(arg->progname()+" [-Ddp] [-a addr] [[tag:]score]");
	while((ch := arg->opt()) != 0)
		case ch {
		'D' =>	styxservers->traceset(1);
		'a' =>	addr = arg->earg();
		'd' =>	vac->dflag = dflag++;
		'p' =>	pflag++;
		* =>	arg->usage();
		}
	args = arg->argv();
	if(len args > 1)
		arg->usage();

	score: ref Score;
	if(len args == 1) {
		(tag, scorestr) := str->splitstrr(hd args, ":");
		if(tag != nil)
			tag = tag[:len tag-1];
		if(tag == nil)
			tag = "vac";
		if(tag != "vac")
			fail("bad score type: "+tag);
		(ok, s) := Score.parse(scorestr);
		if(ok != 0)
			fail("bad score: "+scorestr);
		score = ref s;
	}

	addr = dial->netmkaddr(addr, "net", "venti");
	cc := dial->dial(addr, nil);
	if(cc == nil)
		fail(sprint("dialing %s: %r", addr));
	say("have connection");

	fd := cc.dfd;
	session = Session.new(fd);
	if(session == nil)
		fail(sprint("handshake: %r"));
	say("have handshake");

	rqid := 0;
	red: ref Elem;
	if(args == nil) {
		de := Direntry.new();
		de.uid = de.gid = de.mid = user();
		de.ctime = de.atime = de.mtime = dt->now();
		de.mode = Vac->Modedir|8r755;
		de.emode = Sys->DMDIR|8r755;
		red = ref Elem.Dir(rqid, de, big 0, nil, rqid, 0, 0, nil);
	} else {
		(vd, de, err) := vac->vdroot(session, *score);
		if(err != nil)
			fail(err);
		rqid = ++lastqid;
		red = ref Elem.Dir(rqid, de, big 0, vd, rqid, 0, 0, nil);
	}
	set(red);
	say(sprint("have root, qid=%d", rqid));

	navchan := chan of ref Navop;
	nav := Navigator.new(navchan);
	spawn navigator(navchan);

	msgc: chan of ref Tmsg;
	(msgc, ss) = Styxserver.new(sys->fildes(0), nav, big rqid);

	for(;;) {
		mm := <-msgc;
		if(mm == nil)
			fail("eof");

		pick m := mm {
		Readerror =>
			fail("read error: "+m.error);

		Read =>
			if(dflag) say(sprint("have read, offset=%ubd count=%d", m.offset, m.count));
			(c, err) := ss.canread(m);
			if(c == nil){
				ss.reply(ref Rmsg.Error(m.tag, err));
				break;
			}
			if(c.qtype & Sys->QTDIR){
				ss.default(m);
				break;
			}

			ef := getfile(int c.path);
			n := m.count;
			a := array[n] of byte;
			have := ef.vf.pread(a, n, m.offset);
			if(have < 0) {
				ss.reply(ref Rmsg.Error(m.tag, sprint("%r")));
				break;
			}
			ss.reply(ref Rmsg.Read(m.tag, a[:have]));

		Open =>
			(c, mode, f, err) := canopen(m);
			if(c == nil){
				ss.reply(ref Rmsg.Error(m.tag, err));
				break;
			}
			c.open(mode, f.qid);
			ss.reply(ref Rmsg.Open(m.tag, f.qid, ss.iounit()));


		* =>
			ss.default(m);
		}
	}
}

canopen(m: ref Tmsg.Open): (ref Fid, int, ref Sys->Dir, string)
{
	c := ss.getfid(m.fid);
	if(c == nil)
		return (nil, 0, nil, Styxservers->Ebadfid);
	if(c.isopen)
		return (nil, 0, nil, Styxservers->Eopen);
	(f, err) := ss.t.stat(c.path);
	if(f == nil)
		return (nil, 0, nil, err);
	mode := styxservers->openmode(m.mode);
	if(mode == -1)
		return (nil, 0, nil, Styxservers->Ebadarg);
	if(mode != Sys->OREAD && f.qid.qtype & Sys->QTDIR)
		return (nil, 0, nil, Styxservers->Eperm);
	if(!pflag && !styxservers->openok(c.uname, m.mode, f.mode, f.uid, f.gid))
		return (nil, 0, nil, Styxservers->Eperm);
	if(m.mode & Sys->ORCLOSE)
		return (nil, 0, nil, Styxservers->Eperm);
	return (c, mode, f, err);
}

navigator(c: chan of ref Navop)
{
loop:
	for(;;) {
		navop := <- c;
		if(dflag) say(sprint("have navop, path=%bd", navop.path));
		pick n := navop {
		Stat =>
			if(dflag) say(sprint("have stat"));
			n.reply <-= (get(int n.path).stat(), nil);

		Walk =>
			if(dflag) say(sprint("have walk, name=%q", n.name));
			ed := getdir(int n.path);
			(ne, err) := walk(ed, n.name);
			if(err != nil) {
				n.reply <-= (nil, err);
				break;
			}
			n.reply <-= (ne.stat(), nil);

		Readdir =>
			if(dflag) say(sprint("have readdir path=%bd offset=%d count=%d", n.path, n.offset, n.count));
			if(n.path == big 0) {
				n.reply <-= (nil, nil);
				break;
			}
			ed := getdir(int n.path);
			if(n.offset == 0) {
				ed.vd.rewind();
				ed.offset = 0;
				ed.nprev = 0;
				ed.prev = array[0] of ref Sys->Dir;
			}
			skip := n.offset-ed.offset;
			if(skip > 0) {
				ed.prev = ed.prev[skip:];
				ed.nprev -= skip;
				ed.offset += skip;
			}
			if(len ed.prev < n.count) {
				newprev := array[n.count] of ref Sys->Dir;
				newprev[:] = ed.prev;
				ed.prev = newprev;
			}
			while(ed.nprev < n.count) {
				(ok, de) := ed.vd.readdir();
				if(ok < 0) {
					say(sprint("readdir error: %r"));
					n.reply <-= (nil, sprint("reading directory: %r"));
					continue loop;
				}
				if(de == nil)
					break;
				ne := childget(ed.qid, de.elem);
				if(ne == nil) {
					nqid := childput(ed.qid, de.elem);
					ne = Elem.new(nqid, ed.vd, de, ed.qid);
					if(ne == nil) {
						n.reply <-= (nil, sprint("%r"));
						continue loop;
					}
					set(ne);
				}
				d := ne.stat();
				ed.prev[ed.nprev++] = d;
				n.reply <-= (d, nil);
			}
			n.reply <-= (nil, nil);
		}
	}
}

user(): string
{
	fd := sys->open("/dev/user", Sys->OREAD);
	if(fd != nil)
	if((n := sys->read(fd, d := array[128] of byte, len d)) > 0)
		return string d[:n];
	return "nobody";
}

pid(): int
{
	return sys->pctl(0, nil);
}

killgrp(pid: int)
{
	sys->fprint(sys->open(sprint("/prog/%d/ctl", pid), sys->OWRITE), "killgrp");
}

say(s: string)
{
	if(dflag)
		warn(s);
}

fd2: ref Sys->FD;
warn(s: string)
{
	if(fd2 == nil)
		fd2 = sys->fildes(2);
	sys->fprint(fd2, "%s\n", s);
}

fail(s: string)
{
	warn(s);
	killgrp(pid());
	raise "fail:"+s;
}