code: purgatorio

ref: 42dfac6916ebbdac65cbec8b3e1a80c3ee41423c
dir: /appl/lib/nametree.b/

View raw version
implement Nametree;
include "sys.m";
	sys: Sys;
include "styx.m";
include "styxservers.m";
	Navop: import Styxservers;
	Enotfound, Eexists: import Styxservers;

Fholder: adt {
	parentqid:	big;
	d:		Sys->Dir;
	child:	cyclic ref Fholder;
	sibling:	cyclic ref Fholder;
	hash:	cyclic ref Fholder;
};

init()
{
	sys = load Sys Sys->PATH;
}

start(): (ref Tree, chan of ref Styxservers->Navop)
{
	fs := ref Tree(chan of ref Treeop, chan of string);
	c := chan of ref Styxservers->Navop;
	spawn fsproc(c, fs.c);
	return (fs, c);
}

Tree.quit(t: self ref Tree)
{
	t.c <-= nil;
}

Tree.create(t: self ref Tree, parentq: big, d: Sys->Dir): string
{
	t.c <-= ref Treeop.Create(t.reply, parentq, d);
	return <-t.reply;
}

Tree.remove(t: self ref Tree, q: big): string
{
	t.c <-= ref Treeop.Remove(t.reply, q);
	return <-t.reply;
}

Tree.wstat(t: self ref Tree, q: big, d: Sys->Dir): string
{
	t.c <-= ref Treeop.Wstat(t.reply, q, d);
	return <-t.reply;
}

Tree.getpath(t: self ref Tree, q: big): string
{
	t. c <-= ref Treeop.Getpath(t.reply, q);
	return <-t.reply;
}

fsproc(c: chan of ref Styxservers->Navop, fsc: chan of ref Treeop)
{
	tab := array[23] of ref Fholder;

	for (;;) alt {
	grq := <-c =>
		if (grq == nil)
			exit;
		(q, reply) := (grq.path, grq.reply);
		fh := findfile(tab, q);
		if (fh == nil) {
			reply <-= (nil, Enotfound);
			continue;
		}
		pick rq := grq {
		Stat =>
			reply <-= (ref fh.d, nil);
		Walk =>
			d := fswalk(tab, fh, rq.name);
			if (d == nil)
				reply <-= (nil, Enotfound);
			else
				reply <-= (d, nil);
		Readdir =>
			(start, end) := (rq.offset, rq.offset + rq.count);
			fh = fh.child;
			for (i := 0; i < end && fh != nil; i++) {
				if (i >= start)
					reply <-= (ref fh.d, nil);
				fh = fh.sibling;
			}
			reply <-= (nil, nil);
		* =>
			panic(sys->sprint("unknown op %d\n", tagof(grq)));
		}
	grq := <-fsc =>
		if (grq == nil)
			exit;
		(q, reply) := (grq.q, grq.reply);
		pick rq := grq {
		Create =>
			reply <-= fscreate(tab, q, rq.d);
		Remove =>
			reply <-= fsremove(tab, q);
		Wstat =>
			reply <-= fswstat(tab, q, rq.d);
		Getpath =>
			reply <-= fsgetpath(tab, q);
		* =>
			panic(sys->sprint("unknown fs op %d\n", tagof(grq)));
		}
	}
}

hashfn(q: big, n: int): int
{
	h := int (q % big n);
	if (h < 0)
		h += n;
	return h;
}

findfile(tab: array of ref Fholder, q: big): ref Fholder
{
	for (fh := tab[hashfn(q, len tab)]; fh != nil; fh = fh.hash)
		if (fh.d.qid.path == q)
			return fh;
	return nil;
}

fsgetpath(tab: array of ref Fholder, q: big): string
{
	fh := findfile(tab, q);
	if (fh == nil)
		return nil;
	s := fh.d.name;
	while (fh.parentqid != fh.d.qid.path) {
		fh = findfile(tab, fh.parentqid);
		if (fh == nil)
			return nil;
		s = fh.d.name + "/" + s;
	}
	return s;
}

fswalk(tab: array of ref Fholder, fh: ref Fholder, name: string): ref Sys->Dir
{
	if (name == "..")
		return ref findfile(tab, fh.parentqid).d;
	for (fh = fh.child; fh != nil; fh = fh.sibling)
		if (fh.d.name == name)
			return ref fh.d;
	return nil;
}

fsremove(tab: array of ref Fholder, q: big): string
{
	prev: ref Fholder;

	# remove from hash table
	slot := hashfn(q, len tab);
	for (fh := tab[slot]; fh != nil; fh = fh.hash) {
		if (fh.d.qid.path == q)
			break;
		prev = fh;
	}
	if (fh == nil)
		return Enotfound;
	if (prev == nil)
		tab[slot] = fh.hash;
	else
		prev.hash = fh.hash;
	fh.hash = nil;

	# remove from parent's children
	parent := findfile(tab, fh.parentqid);
	if (parent != nil) {
		prev = nil;
		for (sfh := parent.child; sfh != nil; sfh = sfh.sibling) {
			if (sfh == fh)
				break;
			prev = sfh;
		}
		if (sfh == nil)
			panic("child not found in parent");
		if (prev == nil)
			parent.child = fh.sibling;
		else
			prev.sibling = fh.sibling;
	}
	fh.sibling = nil;

	# now remove any descendents
	sibling: ref Fholder;
	for (sfh := fh.child; sfh != nil; sfh = sibling) {
		sibling = sfh.sibling;
		sfh.parentqid = sfh.d.qid.path;		# make sure it doesn't disrupt things.
		fsremove(tab, sfh.d.qid.path);
	}
	return nil;
}

fscreate(tab: array of ref Fholder, q: big, d: Sys->Dir): string
{
	parent := findfile(tab, q);
	if (findfile(tab, d.qid.path) != nil)
		return Eexists;
	# allow creation of a root directory only if its parent is itself
	if (parent == nil && d.qid.path != q)
		return Enotfound;
	fh: ref Fholder;
	if (parent == nil)
		fh = ref Fholder(q, d, nil, nil, nil);
	else {
		if (fswalk(tab, parent, d.name) != nil)
			return Eexists;
		fh = ref Fholder(parent.d.qid.path, d, nil, nil, nil);
		fh.sibling = parent.child;
		parent.child = fh;
	}
	slot := hashfn(d.qid.path, len tab);
	fh.hash = tab[slot];
	tab[slot] = fh;
	return nil;
}

fswstat(tab: array of ref Fholder, q: big, d: Sys->Dir): string
{
	fh := findfile(tab, q);
	if (fh == nil)
		return Enotfound;

	d = applydir(d, fh.d);

	# if renaming a file, check for duplicates
	if (d.name != fh.d.name) {
		parent := findfile(tab, fh.parentqid);
		if (parent != nil && parent != fh && fswalk(tab, parent, d.name) != nil)
			return Eexists;
	}
	fh.d = d;
	fh.d.qid.path = q;		# ensure the qid can't be changed
	return nil;
}

applydir(d: Sys->Dir, onto: Sys->Dir): Sys->Dir
{
	if (d.name != nil)
		onto.name = d.name;
	if (d.uid != nil)
		onto.uid = d.uid;
	if (d.gid != nil)
		onto.gid = d.gid;
	if (d.muid != nil)
		onto.muid = d.muid;
	if (d.qid.vers != ~0)
		onto.qid.vers = d.qid.vers;
	if (d.qid.qtype != ~0)
		onto.qid.qtype = d.qid.qtype;
	if (d.mode != ~0)
		onto.mode = d.mode;
	if (d.atime != ~0)
		onto.atime = d.atime;
	if (d.mtime != ~0)
		onto.mtime = d.mtime;
	if (d.length != ~big 0)
		onto.length = d.length;
	if (d.dtype != ~0)
		onto.dtype = d.dtype;
	if (d.dev != ~0)
		onto.dev = d.dev;
	return onto;
}

panic(s: string)
{
	sys->fprint(sys->fildes(2), "panic: %s\n", s);
	raise "panic";
}