diff options
Diffstat (limited to 'appl/cmd/vacfs.b')
| -rw-r--r-- | appl/cmd/vacfs.b | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/appl/cmd/vacfs.b b/appl/cmd/vacfs.b new file mode 100644 index 00000000..5fffc98d --- /dev/null +++ b/appl/cmd/vacfs.b @@ -0,0 +1,446 @@ +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"); +} |
