implement Vacfs; include "sys.m"; sys: Sys;include "draw.m";include "arg.m";include "string.m";include "daytime.m";include "venti.m";include "vac.m";include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx;include "styxservers.m"; str: String;daytime: Daytime;venti: Venti;vac: Vac;styxservers: Styxservers; print, sprint, fprint, fildes: import sys;Score, Session: import venti;Roottype, Dirtype, Pointertype0, Datatype: import venti;Root, Entry, Direntry, Metablock, Metaentry, Entrysize, Modeperm, Modeappend, Modeexcl, Modedir, Modesnapshot, Vacdir, Vacfile, Source: import vac;Styxserver, Fid, Navigator, Navop, Enotfound: import styxservers; Vacfs: module { init: fn(nil: ref Draw->Context, args: list of string);}; addr := "net!$venti!venti";dflag := pflag := 0;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 (big, int);}; 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, vqid: big): 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) { (vq, cq) := hd m; if(vq == vqid) return get(cq); } } return nil;} childput(qid: int, vqid: big): 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 = (vqid, ++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.qid); if(ne == nil) { nqid := childput(ed.qid, de.qid); ne = Elem.new(nqid, ed.vd, de, ed.qid); 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; } error("internal error, getfile"); return nil;} getdir(qid: int): ref Elem.Dir{ pick d := get(qid) { Dir => return d; } error("internal error, getdir"); return nil;} init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; arg := load Arg Arg->PATH; str = load String String->PATH; daytime = load Daytime Daytime->PATH; venti = load Venti Venti->PATH; styx = load Styx Styx->PATH; styxservers = load Styxservers Styxservers->PATH; vac = load Vac Vac->PATH; if(venti == nil || vac == nil) error("loading venti,vac"); sys->pctl(sys->NEWPGRP, nil); venti->init(); vac->init(); styx->init(); styxservers->init(styx); 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' => dflag++; vac->dflag++; 'p' => pflag++; * => warn(sprint("bad option: -%c", ch)); 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") error("bad score type: "+tag); (ok, s) := Score.parse(scorestr); if(ok != 0) error("bad score: "+scorestr); score = ref s; } (cok, conn) := sys->dial(addr, nil); if(cok < 0) error(sprint("dialing %s: %r", addr)); say("have connection"); fd := conn.dfd; session = Session.new(fd); if(session == nil) error(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 = daytime->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) error(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(;;) { pick m := <- msgc { Readerror => say("read error: "+m.error); Read => 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; say(sprint("have navop, path=%bd", navop.path)); pick n := navop { Stat => say(sprint("have stat")); n.reply <-= (get(int n.path).stat(), nil); Walk => 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 => 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.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.qid); if(ne == nil) { nqid := childput(ed.qid, de.qid); ne = Elem.new(nqid, ed.vd, de, ed.qid); } d := ne.stat(); ed.prev[ed.nprev++] = d; n.reply <-= (d, nil); } n.reply <-= (nil, nil); } }} user(): string{ if((fd := sys->open("/dev/user", Sys->OREAD)) != nil && (n := sys->read(fd, d := array[128] of byte, len d)) > 0) return string d[:n]; return "nobody";} error(s: string){ killgrp(); fprint(fildes(2), "%s\n", s); raise "fail:"+s;} warn(s: string){ fprint(fildes(2), "%s\n", s);} say(s: string){ if(dflag) warn(s);} killgrp(){ fd := sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", sys->OWRITE); if(fd != nil) sys->fprint(fd, "killgrp\n");}
implement Vacfs;
include "sys.m";
sys: Sys;
include "draw.m";
include "arg.m";
include "string.m";
include "daytime.m";
include "venti.m";
include "vac.m";
include "styx.m";
styx: Styx;
Tmsg, Rmsg: import styx;
include "styxservers.m";
str: String;
daytime: Daytime;
venti: Venti;
vac: Vac;
styxservers: Styxservers;
print, sprint, fprint, fildes: import sys;
Score, Session: import venti;
Roottype, Dirtype, Pointertype0, Datatype: import venti;
Root, Entry, Direntry, Metablock, Metaentry, Entrysize, Modeperm, Modeappend, Modeexcl, Modedir, Modesnapshot, Vacdir, Vacfile, Source: import vac;
Styxserver, Fid, Navigator, Navop, Enotfound: import styxservers;
Vacfs: module {
init: fn(nil: ref Draw->Context, args: list of string);
};
addr := "net!$venti!venti";
dflag := pflag := 0;
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 (big, int);
};
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, vqid: big): 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) {
(vq, cq) := hd m;
if(vq == vqid)
return get(cq);
}
}
return nil;
}
childput(qid: int, vqid: big): 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 = (vqid, ++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.qid);
if(ne == nil) {
nqid := childput(ed.qid, de.qid);
ne = Elem.new(nqid, ed.vd, de, ed.qid);
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;
}
error("internal error, getfile");
return nil;
}
getdir(qid: int): ref Elem.Dir
{
pick d := get(qid) {
Dir => return d;
}
error("internal error, getdir");
return nil;
}
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
arg := load Arg Arg->PATH;
str = load String String->PATH;
daytime = load Daytime Daytime->PATH;
venti = load Venti Venti->PATH;
styx = load Styx Styx->PATH;
styxservers = load Styxservers Styxservers->PATH;
vac = load Vac Vac->PATH;
if(venti == nil || vac == nil)
error("loading venti,vac");
sys->pctl(sys->NEWPGRP, nil);
venti->init();
vac->init();
styx->init();
styxservers->init(styx);
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' => dflag++;
vac->dflag++;
'p' => pflag++;
* => warn(sprint("bad option: -%c", ch));
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")
error("bad score type: "+tag);
(ok, s) := Score.parse(scorestr);
if(ok != 0)
error("bad score: "+scorestr);
score = ref s;
}
(cok, conn) := sys->dial(addr, nil);
if(cok < 0)
error(sprint("dialing %s: %r", addr));
say("have connection");
fd := conn.dfd;
session = Session.new(fd);
if(session == nil)
error(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 = daytime->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)
error(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(;;) {
pick m := <- msgc {
Readerror =>
say("read error: "+m.error);
Read =>
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;
say(sprint("have navop, path=%bd", navop.path));
pick n := navop {
Stat =>
say(sprint("have stat"));
n.reply <-= (get(int n.path).stat(), nil);
Walk =>
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 =>
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.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.qid);
if(ne == nil) {
nqid := childput(ed.qid, de.qid);
ne = Elem.new(nqid, ed.vd, de, ed.qid);
}
d := ne.stat();
ed.prev[ed.nprev++] = d;
n.reply <-= (d, nil);
}
n.reply <-= (nil, nil);
}
}
}
user(): string
{
if((fd := sys->open("/dev/user", Sys->OREAD)) != nil
&& (n := sys->read(fd, d := array[128] of byte, len d)) > 0)
return string d[:n];
return "nobody";
}
error(s: string)
{
killgrp();
fprint(fildes(2), "%s\n", s);
raise "fail:"+s;
}
warn(s: string)
{
fprint(fildes(2), "%s\n", s);
}
say(s: string)
{
if(dflag)
warn(s);
}
killgrp()
{
fd := sys->open("/prog/"+string sys->pctl(0, nil)+"/ctl", sys->OWRITE);
if(fd != nil)
sys->fprint(fd, "killgrp\n");
}