diff options
| -rw-r--r-- | CHANGES | 3 | ||||
| -rw-r--r-- | appl/cmd/mkfile | 6 | ||||
| -rw-r--r-- | appl/cmd/vacfs.b | 446 | ||||
| -rw-r--r-- | appl/cmd/vacget.b | 199 | ||||
| -rw-r--r-- | appl/cmd/vacput.b | 258 | ||||
| -rw-r--r-- | appl/lib/mkfile | 2 | ||||
| -rw-r--r-- | appl/lib/vac.b | 1094 | ||||
| -rw-r--r-- | dis/lib/vac.dis | bin | 0 -> 17748 bytes | |||
| -rw-r--r-- | dis/vacfs.dis | bin | 0 -> 8141 bytes | |||
| -rw-r--r-- | dis/vacget.dis | bin | 0 -> 4203 bytes | |||
| -rw-r--r-- | dis/vacput.dis | bin | 0 -> 6585 bytes | |||
| -rw-r--r-- | lib/proto/inferno | 25 | ||||
| -rw-r--r-- | lib/proto/src | 14 | ||||
| -rw-r--r-- | man/1/INDEX | 2 | ||||
| -rw-r--r-- | man/1/vacget | 75 | ||||
| -rw-r--r-- | man/4/INDEX | 1 | ||||
| -rw-r--r-- | man/4/vacfs | 47 | ||||
| -rw-r--r-- | man/lib/checkman.awk | 187 | ||||
| -rw-r--r-- | man/lib/colophon | 26 | ||||
| -rw-r--r-- | man/lib/lookman/junkwords | 509 | ||||
| -rwxr-xr-x | man/lib/lookman/mkindex | 13 | ||||
| -rw-r--r-- | man/lib/notes | 10 | ||||
| -rw-r--r-- | man/lib/preface | 73 | ||||
| -rwxr-xr-x | man/lib/secindex | 43 | ||||
| -rw-r--r-- | man/lib/title | 77 | ||||
| -rw-r--r-- | man/lib/trademarks | 46 | ||||
| -rw-r--r-- | man/mkfile | 119 | ||||
| -rw-r--r-- | module/vac.m | 188 |
28 files changed, 3456 insertions, 7 deletions
@@ -1,5 +1,8 @@ 20070614 /appl/lib/venti.b bug fixes, a few more errstrs, remove prints to stderr + add initial module/vac.m appl/lib/vac.b from mjl (gsoc project ventivac) + add initial /appl/cmd/^(vacfs.b vacget.b vacput.b) /man/4/vacfs /man/1/vacget from mjl (gsoc:ventivac) + include omitted man/mkfile man/lib 20070608 update /lib9 functions to use silly va_copy (and then va_end) instead of just assigning, to account for silly C implementations change /appl/cmd/mc.b not to require Draw or Env (so lc works on smaller systems) diff --git a/appl/cmd/mkfile b/appl/cmd/mkfile index 69cf60d0..29c1679f 100644 --- a/appl/cmd/mkfile +++ b/appl/cmd/mkfile @@ -160,6 +160,9 @@ TARG=\ unmount.dis\ uudecode.dis\ uuencode.dis\ + vacfs.dis\ + vacget.dis\ + vacput.dis\ wav2iaf.dis\ wc.dis\ webgrab.dis\ @@ -216,3 +219,6 @@ rawdbfs.dis: $MODDIR/styxservers.m import.dis: $MODDIR/encoding.m $MODDIR/factotum.m basename.dis: $MODDIR/names.m cleanname.dis: $MODDIR/names.m +vacfs.dis: $MODDIR/vac.m $MODDIR/venti.m +vacget.dis: $MODDIR/vac.m $MODDIR/venti.m +vacput.dis: $MODDIR/vac.m $MODDIR/venti.m 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"); +} diff --git a/appl/cmd/vacget.b b/appl/cmd/vacget.b new file mode 100644 index 00000000..c14ad28a --- /dev/null +++ b/appl/cmd/vacget.b @@ -0,0 +1,199 @@ +implement Vacget; + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "arg.m"; +include "string.m"; +include "venti.m"; +include "vac.m"; + +str: String; +venti: Venti; +vac: Vac; + +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; + +Vacget: module { + init: fn(nil: ref Draw->Context, args: list of string); +}; + +addr := "net!$venti!venti"; +dflag := vflag := pflag := tflag := 0; +session: ref Session; + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + arg := load Arg Arg->PATH; + str = load String String->PATH; + venti = load Venti Venti->PATH; + vac = load Vac Vac->PATH; + if(venti == nil || vac == nil) + error("loading venti,vac"); + venti->init(); + vac->init(); + + arg->init(args); + arg->setusage(sprint("%s [-dtv] [-a addr] [tag:]score", arg->progname())); + while((c := arg->opt()) != 0) + case c { + 'a' => addr = arg->earg(); + 'd' => dflag++; + vac->dflag++; + 'p' => pflag++; + 't' => tflag++; + 'v' => vflag++; + * => warn(sprint("bad option: -%c", c)); + arg->usage(); + } + args = arg->argv(); + if(len args != 1) + arg->usage(); + + (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); + + (sok, score) := Score.parse(scorestr); + if(sok != 0) + error("bad score: "+scorestr); + say("have score"); + + (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"); + + (vd, nil, err) := vac->vdroot(session, score); + if(err != nil) + error(err); + + say("starting walk"); + walk(".", vd); +} + +create(path: string, omode: int, de: ref Direntry): ref Sys->FD +{ + perm := Sys->DMDIR | Sys->DMAPPEND | Sys->DMEXCL | Sys->DMTMP; + perm &= de.emode; + perm |= 8r666; + if(de.emode & Sys->DMDIR) + perm |= 8r777; + fd := sys->create(path, omode, perm); + if(fd == nil) + return nil; + if(pflag) { + d := sys->nulldir; + d.uid = de.uid; + d.gid = de.gid; + d.mode = de.emode; + if(sys->fwstat(fd, d) != 0) { + warn(sprint("fwstat %s for uid/gid/mode: %r", path)); + d.uid = d.gid = ""; + sys->fwstat(fd, d); + } + } + return fd; +} + +walk(path: string, vd: ref Vacdir) +{ + say("start of walk: "+path); + for(;;) { + (n, de) := vd.readdir(); + if(n < 0) + error(sprint("reading direntry in %s: %r", path)); + if(n == 0) + break; + say("walk: have direntry, elem="+de.elem); + newpath := path+"/"+de.elem; + (e, me) := vd.open(de); + if(e == nil) + error(sprint("reading entry for %s: %r", newpath)); + + oflags := de.mode&~(Modeperm|Modeappend|Modeexcl|Modedir|Modesnapshot); + if(oflags) + warn(sprint("%s: not all bits in mode can be set: 0x%x", newpath, oflags)); + + if(tflag || vflag) + print("%s\n", newpath); + + if(me != nil) { + if(!tflag) + create(newpath, Sys->OREAD, de); + # ignore error, possibly for already existing dir. + # if creating really failed, writing files in the dir will fail later on. + walk(newpath, Vacdir.new(session, e, me)); + } else { + if(tflag) + continue; + say("writing file"); + fd := create(newpath, sys->OWRITE, de); + if(fd == nil) + error(sprint("creating %s: %r", newpath)); + bio := bufio->fopen(fd, bufio->OWRITE); + if(bio == nil) + error(sprint("bufio fopen %s: %r", newpath)); + + buf := array[sys->ATOMICIO] of byte; + vf := Vacfile.new(session, e); + for(;;) { + rn := vf.read(buf, len buf); + if(rn == 0) + break; + if(rn < 0) + error(sprint("reading vac %s: %r", newpath)); + wn := bio.write(buf, rn); + if(wn != rn) + error(sprint("writing local %s: %r", newpath)); + } + bok := bio.flush(); + bio.close(); + if(bok == bufio->ERROR || bok == bufio->EOF) + error(sprint("bufio close: %r")); + + if(pflag) { + d := sys->nulldir; + d.mtime = de.mtime; + if(sys->fwstat(fd, d) < 0) + warn(sprint("fwstat %s for mtime: %r", newpath)); + } + fd = nil; + } + } +} + +error(s: string) +{ + 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); +} diff --git a/appl/cmd/vacput.b b/appl/cmd/vacput.b new file mode 100644 index 00000000..e01bdd36 --- /dev/null +++ b/appl/cmd/vacput.b @@ -0,0 +1,258 @@ +implement Vacput; + +include "sys.m"; + sys: Sys; +include "draw.m"; +include "daytime.m"; +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; +include "arg.m"; +include "string.m"; +include "venti.m"; +include "vac.m"; + +daytime: Daytime; +str: String; +venti: Venti; +vac: Vac; + +print, sprint, fprint, fildes: import sys; +Score, Session: import venti; +Roottype, Dirtype, Pointertype0, Datatype: import venti; +Root, Entry, Direntry, Metablock, Metaentry, Entrysize, File, Sink, MSink: import vac; + +Vacput: module { + init: fn(nil: ref Draw->Context, args: list of string); +}; + +addr := "net!$venti!venti"; +dflag := 0; +vflag := 0; +blocksize := vac->Dsize; +session: ref Session; +name := "vac"; + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + daytime = load Daytime Daytime->PATH; + bufio = load Bufio Bufio->PATH; + arg := load Arg Arg->PATH; + str = load String String->PATH; + venti = load Venti Venti->PATH; + vac = load Vac Vac->PATH; + if(venti == nil || vac == nil) + error("loading venti,vac"); + venti->init(); + vac->init(); + + arg->init(args); + arg->setusage(sprint("%s [-dtv] [-a addr] [-b blocksize] [-n name] path ...", arg->progname())); + while((c := arg->opt()) != 0) + case c { + 'a' => addr = arg->earg(); + 'b' => blocksize = int arg->earg(); + 'n' => name = arg->earg(); + 'd' => dflag++; + vac->dflag++; + 'v' => vflag++; + * => warn(sprint("bad option: -%c", c)); + arg->usage(); + } + args = arg->argv(); + if(len args == 0) + arg->usage(); + + (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"); + + topde: ref Direntry; + if(len args == 1 && ((nil, d) := sys->stat(hd args)).t0 == 0 && d.mode&Sys->DMDIR) { + topde = Direntry.mk(d); + topde.elem = name; + } else { + topde = Direntry.new(); + topde.elem = name; + topde.uid = topde.gid = user(); + topde.mode = 8r777|Vac->Modedir; + topde.mtime = topde.atime = 0; + } + topde.ctime = daytime->now(); + + s := Sink.new(session, blocksize); + ms := MSink.new(session, blocksize); + for(; args != nil; args = tl args) + writepath(hd args, s, ms); + say("tree written"); + + e0 := s.finish(); + if(e0 == nil) + error(sprint("writing top entry: %r")); + e1 := ms.finish(); + if(e1 == nil) + error(sprint("writing top meta entry: %r")); + say(sprint("top entries written (%s, %s)", e0.score.text(), e1.score.text())); + s2 := MSink.new(session, blocksize); + if(s2.add(topde) < 0) + error(sprint("adding direntry for top entries: %r")); + e2 := s2.finish(); + say("top meta entry written, "+e2.score.text()); + + td := array[Entrysize*3] of byte; + td[0*Entrysize:] = e0.pack(); + td[1*Entrysize:] = e1.pack(); + td[2*Entrysize:] = e2.pack(); + (tok, tscore) := session.write(Dirtype, td); + if(tok < 0) + error(sprint("writing top-level entries: %r")); + say("top entry written, "+tscore.text()); + + root := Root.new(name, "vac", tscore, blocksize, nil); + rd := root.pack(); + if(rd == nil) + error(sprint("root pack: %r")); + (rok, rscore) := session.write(Roottype, rd); + if(rok < 0) + error(sprint("writing root score: %r")); + say("root written, "+rscore.text()); + print("vac:%s\n", rscore.text()); + if(session.sync() < 0) + error(sprint("syncing server: %r")); +} + +writepath(path: string, s: ref Sink, ms: ref MSink) +{ + if(vflag) + print("%s\n", path); +say("writepath "+path); + fd := sys->open(path, sys->OREAD); + if(fd == nil) + error(sprint("opening %s: %r", path)); + (ok, dir) := sys->fstat(fd); + if(ok < 0) + error(sprint("fstat %s: %r", path)); +say("writepath: file opened"); + if(dir.mode&sys->DMAUTH) { + warn(path+": is auth file, skipping"); + return; + } + if(dir.mode&sys->DMTMP) { + warn(path+": is temporary file, skipping"); + return; + } + + e, me: ref Entry; + de: ref Direntry; + if(dir.mode & sys->DMDIR) { +say("writepath: file is dir"); + ns := Sink.new(session, blocksize); + nms := MSink.new(session, blocksize); + for(;;) { + (n, dirs) := sys->dirread(fd); + if(n == 0) + break; + if(n < 0) + error(sprint("dirread %s: %r", path)); + for(i := 0; i < len dirs; i++) { + d := dirs[i]; + npath := path+"/"+d.name; + writepath(npath, ns, nms); + } + } + e = ns.finish(); + if(e == nil) + error(sprint("error flushing dirsink for %s: %r", path)); + me = nms.finish(); + if(me == nil) + error(sprint("error flushing metasink for %s: %r", path)); + } else { +say("writepath: file is normale file"); + e = writefile(path, fd); + if(e == nil) + error(sprint("error flushing filesink for %s: %r", path)); + } +say("writepath: wrote path, "+e.score.text()); + + de = Direntry.mk(dir); +say("writepath: have direntry"); + + i := s.add(e); + if(i < 0) + error(sprint("adding entry to sink: %r")); + mi := 0; + if(me != nil) + mi = s.add(me); + if(mi < 0) + error(sprint("adding mentry to sink: %r")); + de.entry = i; + de.mentry = mi; + i = ms.add(de); + if(i < 0) + error(sprint("adding direntry to msink: %r")); +say("writepath done"); +} + +writefile(path: string, fd: ref Sys->FD): ref Entry +{ + bio := bufio->fopen(fd, bufio->OREAD); + if(bio == nil) + error(sprint("bufio opening %s: %r", path)); + say(sprint("bufio opened path %s", path)); + + f := File.new(session, Datatype, blocksize); + for(;;) { + buf := array[blocksize] of byte; + n := 0; + while(n < len buf) { + want := len buf - n; + have := bio.read(buf[n:], want); + if(have == 0) + break; + if(have < 0) + error(sprint("reading %s: %r", path)); + n += have; + } + say(sprint("have buf, length %d", n)); + + if(f.write(buf[:n]) < 0) + error(sprint("writing %s: %r", path)); + if(n != len buf) + break; + } + bio.close(); + return f.finish(); +} + +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) +{ + warn(s); + raise "fail:"+s; +} + +warn(s: string) +{ + fprint(fildes(2), "%s\n", s); +} + +say(s: string) +{ + if(dflag) + warn(s); +} diff --git a/appl/lib/mkfile b/appl/lib/mkfile index a5736dbf..a6eaf912 100644 --- a/appl/lib/mkfile +++ b/appl/lib/mkfile @@ -137,6 +137,7 @@ TARG=\ translate.dis\ ubfa.dis\ url.dis\ + vac.dis\ venti.dis\ virgil.dis\ volume.dis\ @@ -232,3 +233,4 @@ rfc822.dis: $ROOT/module/rfc822.m csv.dis: $ROOT/module/csv.m json.dis: $ROOT/module/json.m lists.dis: $ROOT/module/lists.m +vac.dis: $ROOT/module/vac.m $ROOT/module/venti.m diff --git a/appl/lib/vac.b b/appl/lib/vac.b new file mode 100644 index 00000000..53967661 --- /dev/null +++ b/appl/lib/vac.b @@ -0,0 +1,1094 @@ +implement Vac; + +include "sys.m"; +include "venti.m"; +include "vac.m"; + +sys: Sys; +venti: Venti; + +werrstr, sprint, fprint, fildes: import sys; +Roottype, Dirtype, Pointertype0, Datatype: import venti; +Score, Session, Scoresize: import venti; + +dflag = 0; + +# from venti.b +BIT8SZ: con 1; +BIT16SZ: con 2; +BIT32SZ: con 4; +BIT48SZ: con 6; +BIT64SZ: con 8; + +Rootnamelen: con 128; +Rootversion: con 2; +Direntrymagic: con 16r1c4d9072; +Metablockmagic: con 16r5656fc79; +Maxstringsize: con 1000; + +blankroot: Root; +blankentry: Entry; +blankdirentry: Direntry; +blankmetablock: Metablock; +blankmetaentry: Metaentry; + + +init() +{ + sys = load Sys Sys->PATH; + venti = load Venti Venti->PATH; + venti->init(); +} + +pstring(a: array of byte, o: int, s: string): int +{ + sa := array of byte s; # could do conversion ourselves + n := len sa; + a[o] = byte (n >> 8); + a[o+1] = byte n; + a[o+2:] = sa; + return o+BIT16SZ+n; +} + +gstring(a: array of byte, o: int): (string, int) +{ + if(o < 0 || o+BIT16SZ > len a) + return (nil, -1); + l := (int a[o] << 8) | int a[o+1]; + if(l > Maxstringsize) + return (nil, -1); + o += BIT16SZ; + e := o+l; + if(e > len a) + return (nil, -1); + return (string a[o:e], e); +} + +gtstring(a: array of byte, o: int, n: int): string +{ + e := o + n; + if(e > len a) + return nil; + for(i := o; i < e; i++) + if(a[i] == byte 0) + break; + return string a[o:i]; +} + + +Root.new(name, rtype: string, score: Score, blocksize: int, prev: ref Score): ref Root +{ + return ref Root(Rootversion, name, rtype, score, blocksize, prev); +} + +Root.pack(r: self ref Root): array of byte +{ + d := array[Rootsize] of byte; + i := 0; + i = p16(d, i, r.version); + i = ptstring(d, i, r.name, Rootnamelen); + if(i < 0) + return nil; + i = ptstring(d, i, r.rtype, Rootnamelen); + if(i < 0) + return nil; + i = pscore(d, i, r.score); + i = p16(d, i, r.blocksize); + if(r.prev == nil) { + for(j := 0; j < Scoresize; j++) + d[i+j] = byte 0; + i += Scoresize; + } else + i = pscore(d, i, *r.prev); + if(i != len d) { + sys->werrstr("root pack, bad length: "+string i); + return nil; + } + return d; +} + +Root.unpack(d: array of byte): ref Root +{ + if(len d != Rootsize){ + sys->werrstr("root entry is wrong length"); + return nil; + } + r := ref blankroot; + r.version = g16(d, 0); + if(r.version != Rootversion){ + sys->werrstr("unknown root version"); + return nil; + } + o := BIT16SZ; + r.name = gtstring(d, o, Rootnamelen); + o += Rootnamelen; + r.rtype = gtstring(d, o, Rootnamelen); + o += Rootnamelen; + r.score = gscore(d, o); + o += Scoresize; + r.blocksize = g16(d, o); + o += BIT16SZ; + r.prev = ref gscore(d, o); + return r; +} + +Entry.new(psize, dsize, flags: int, size: big, score: Venti->Score): ref Entry +{ + return ref Entry(0, psize, dsize, (flags&Entrydepthmask)>>Entrydepthshift, flags, size, score); +} + +Entry.pack(e: self ref Entry): array of byte +{ + d := array[Entrysize] of byte; + i := 0; + i = p32(d, i, e.gen); + i = p16(d, i, e.psize); + i = p16(d, i, e.dsize); + e.flags |= e.depth<<Entrydepthshift; + d[i++] = byte e.flags; + for(j := 0; j < 5; j++) + d[i++] = byte 0; + i = p48(d, i, e.size); + i = pscore(d, i, e.score); + if(i != len d) { + werrstr(sprint("bad length, have %d, want %d", i, len d)); + return nil; + } + return d; +} + +Entry.unpack(d: array of byte): ref Entry +{ + if(len d != Entrysize){ + sys->werrstr("entry is wrong length"); + return nil; + } + e := ref blankentry; + i := 0; + e.gen = g32(d, i); + i += BIT32SZ; + e.psize = g16(d, i); + i += BIT16SZ; + e.dsize = g16(d, i); + i += BIT16SZ; + e.flags = int d[i]; + e.depth = (e.flags & Entrydepthmask) >> Entrydepthshift; + e.flags &= ~Entrydepthmask; + i += BIT8SZ; + i += 5; # skip something... + e.size = g48(d, i); + i += BIT48SZ; + e.score = gscore(d, i); + i += Scoresize; + if((e.flags & Entryactive) == 0) + return e; + if(!checksize(e.psize) || !checksize(e.dsize)){ + sys->werrstr(sys->sprint("bad blocksize (%d or %d)", e.psize, e.dsize)); + return nil; + } + return e; +} + +Direntry.new(): ref Direntry +{ + return ref Direntry(9, "", 0, 0, 0, 0, big 0, "", "", "", 0, 0, 0, 0, 0, 0); +} + +Direntry.mk(d: Sys->Dir): ref Direntry +{ + atime := 0; # d.atime; + mode := d.mode&Modeperm; + if(d.mode&sys->DMAPPEND) + mode |= Modeappend; + if(d.mode&sys->DMEXCL) + mode |= Modeexcl; + if(d.mode&sys->DMDIR) + mode |= Modedir; + if(d.mode&sys->DMTMP) + mode |= Modetemp; + return ref Direntry(9, d.name, 0, 0, 0, 0, d.qid.path, d.uid, d.gid, d.muid, d.mtime, 0, 0, atime, mode, d.mode); +} + +Direntry.mkdir(de: self ref Direntry): ref Sys->Dir +{ + d := ref sys->nulldir; + d.name = de.elem; + d.uid = de.uid; + d.gid = de.gid; + d.muid = de.mid; + d.qid.path = de.qid; + d.qid.vers = 0; + d.qid.qtype = de.emode>>24; + d.mode = de.emode; + d.atime = de.atime; + d.mtime = de.mtime; + d.length = big 0; + return d; +} + +strlen(s: string): int +{ + return 2+len array of byte s; +} + +Direntry.pack(de: self ref Direntry): array of byte +{ + # assume version 9 + length := 4+2+strlen(de.elem)+4+4+4+4+8+strlen(de.uid)+strlen(de.gid)+strlen(de.mid)+4+4+4+4+4; # + qidspace? + + d := array[length] of byte; + i := 0; + i = p32(d, i, Direntrymagic); + i = p16(d, i, de.version); + i = pstring(d, i, de.elem); + i = p32(d, i, de.entry); + if(de.version == 9) { + i = p32(d, i, de.gen); + i = p32(d, i, de.mentry); + i = p32(d, i, de.mgen); + } + i = p64(d, i, de.qid); + i = pstring(d, i, de.uid); + i = pstring(d, i, de.gid); + i = pstring(d, i, de.mid); + i = p32(d, i, de.mtime); + i = p32(d, i, de.mcount); + i = p32(d, i, de.ctime); + i = p32(d, i, de.atime); + i = p32(d, i, de.mode); + if(i != len d) { + werrstr(sprint("bad length for direntry (expected %d, have %d)", len d, i)); + return nil; + } + return d; +} + +Direntry.unpack(d: array of byte): ref Direntry +{ + { + de := ref blankdirentry; + i := 0; + magic: int; + (magic, i) = eg32(d, i); + if(magic != Direntrymagic) { + werrstr(sprint("bad magic (%x, want %x)", magic, Direntrymagic)); + return nil; + } + (de.version, i) = eg16(d, i); + if(de.version != 8 && de.version != 9) { + werrstr(sprint("bad version (%d)", de.version)); + return nil; + } + (de.elem, i) = egstring(d, i); + (de.entry, i) = eg32(d, i); + case de.version { + 8 => + de.gen = 0; + de.mentry = de.entry+1; + de.mgen = 0; + 9 => + (de.gen, i) = eg32(d, i); + (de.mentry, i) = eg32(d, i); + (de.mgen, i) = eg32(d, i); + } + (de.qid, i) = eg64(d, i); + (de.uid, i) = egstring(d, i); + (de.gid, i) = egstring(d, i); + (de.mid, i) = egstring(d, i); + (de.mtime, i) = eg32(d, i); + (de.mcount, i) = eg32(d, i); + (de.ctime, i) = eg32(d, i); + (de.atime, i) = eg32(d, i); + (de.mode, i) = eg32(d, i); + de.emode = de.mode&Modeperm; + if(de.mode&Modeappend) + de.emode |= sys->DMAPPEND; + if(de.mode&Modeexcl) + de.emode |= sys->DMEXCL; + if(de.mode&Modedir) + de.emode |= sys->DMDIR; + if(de.mode&Modetemp) + de.emode |= sys->DMTMP; + if(de.version == 9) + ; # xxx handle qid space?, can be in here + return de; + } exception e { + "too small:*" => + werrstr("direntry "+e); + return nil; + * => + raise e; + } +} + + +Metablock.new(): ref Metablock +{ + return ref Metablock(0, 0, 0, 0); +} + +Metablock.pack(mb: self ref Metablock, d: array of byte) +{ + i := 0; + i = p32(d, i, Metablockmagic); + i = p16(d, i, mb.size); + i = p16(d, i, mb.free); + i = p16(d, i, mb.maxindex); + i = p16(d, i, mb.nindex); +} + +Metablock.unpack(d: array of byte): ref Metablock +{ + if(len d < Metablocksize) { + werrstr(sprint("bad length for metablock (%d, want %d)", len d, Metablocksize)); + return nil; + } + i := 0; + magic := g32(d, i); + if(magic != Metablockmagic && magic != Metablockmagic+1) { + werrstr(sprint("bad magic for metablock (%x, need %x)", magic, Metablockmagic)); + return nil; + } + i += BIT32SZ; + + mb := ref blankmetablock; + mb.size = g16(d, i); + i += BIT16SZ; + mb.free = g16(d, i); + i += BIT16SZ; + mb.maxindex = g16(d, i); + i += BIT16SZ; + mb.nindex = g16(d, i); + i += BIT16SZ; + if(mb.nindex == 0) { + werrstr("bad metablock, nindex=0"); + return nil; + } + return mb; +} + +Metaentry.pack(me: self ref Metaentry, d: array of byte) +{ + i := 0; + i = p16(d, i, me.offset); + i = p16(d, i, me.size); +} + +Metaentry.unpack(d: array of byte, i: int): ref Metaentry +{ + o := Metablocksize+i*Metaentrysize; + if(o+Metaentrysize > len d) { + werrstr(sprint("meta entry lies outside meta block, i=%d", i)); + return nil; + } + + me := ref blankmetaentry; + me.offset = g16(d, o); + o += BIT16SZ; + me.size = g16(d, o); + o += BIT16SZ; + if(me.offset+me.size > len d) { + werrstr(sprint("meta entry points outside meta block, i=%d", i)); + return nil; + } + return me; +} + + +Page.new(dsize: int): ref Page +{ + psize := (dsize/Scoresize)*Scoresize; + return ref Page(array[psize] of byte, 0); +} + +Page.add(p: self ref Page, s: Score) +{ + for(i := 0; i < Scoresize; i++) + p.d[p.o+i] = s.a[i]; + p.o += Scoresize; +} + +Page.full(p: self ref Page): int +{ + return p.o+Scoresize > len p.d; +} + +Page.data(p: self ref Page): array of byte +{ + for(i := p.o; i >= Scoresize; i -= Scoresize) + if(!Score(p.d[i-Scoresize:i]).eq(Score.zero())) + break; + return p.d[:i]; +} + + +File.new(s: ref Session, dtype, dsize: int): ref File +{ + p := array[1] of ref Page; + p[0] = Page.new(dsize); + return ref File(p, dtype, dsize, big 0, s); +} + +fflush(f: ref File, last: int): (int, ref Entry) +{ + for(i := 0; i < len f.p; i++) { + if(!last && !f.p[i].full()) + return (0, nil); + if(last && f.p[i].o == Scoresize) { + flags := Entryactive; + if(f.dtype & Dirtype) + flags |= Entrydir; + flags |= i<<Entrydepthshift; + score := Score(f.p[i].data()); + if(len score.a == 0) + score = Score.zero(); + return (0, Entry.new(len f.p[i].d, f.dsize, flags, f.size, score)); + } + (ok, score) := f.s.write(Pointertype0+i, f.p[i].data()); + if(ok < 0) + return (-1, nil); + f.p[i] = Page.new(f.dsize); + if(i+1 == len f.p) { + newp := array[len f.p+1] of ref Page; + newp[:] = f.p; + newp[len newp-1] = Page.new(f.dsize); + f.p = newp; + } + f.p[i+1].add(score); + } + werrstr("internal error in fflush"); + return (-1, nil); +} + +File.write(f: self ref File, d: array of byte): int +{ + (fok, nil) := fflush(f, 0); + if(fok < 0) + return -1; + length := len d; + for(i := len d; i > 0; i--) + if(d[i-1] != byte 0) + break; + d = d[:i]; + (ok, score) := f.s.write(f.dtype, d); + if(ok < 0) + return -1; + f.size += big length; + f.p[0].add(score); + return 0; +} + +File.finish(f: self ref File): ref Entry +{ + (ok, e) := fflush(f, 1); + if(ok < 0) + return nil; + return e; +} + + +Sink.new(s: ref Venti->Session, dsize: int): ref Sink +{ + dirdsize := (dsize/Entrysize)*Entrysize; + return ref Sink(File.new(s, Dirtype, dsize), array[dirdsize] of byte, 0, 0); +} + +Sink.add(m: self ref Sink, e: ref Entry): int +{ + ed := e.pack(); + if(ed == nil) + return -1; + n := len m.d - m.nd; + if(n > len ed) + n = len ed; + m.d[m.nd:] = ed[:n]; + m.nd += n; + if(n < len ed) { + if(m.f.write(m.d) < 0) + return -1; + m.nd = len ed - n; + m.d[:] = ed[n:]; + } + return m.ne++; +} + +Sink.finish(m: self ref Sink): ref Entry +{ + if(m.nd > 0) + if(m.f.write(m.d[:m.nd]) < 0) + return nil; + e := m.f.finish(); + e.dsize = len m.d; + return e; +} + + +elemcmp(a, b: array of byte, fossil: int): int +{ + for(i := 0; i < len a && i < len b; i++) + if(a[i] != b[i]) + return (int a[i] - int b[i]); + if(fossil) + return len a - len b; + return len b - len a; +} + +Mentry.cmp(a, b: ref Mentry): int +{ + return elemcmp(array of byte a.elem, array of byte b.elem, 0); +} + +MSink.new(s: ref Venti->Session, dsize: int): ref MSink +{ + return ref MSink(File.new(s, Dirtype, dsize), array[dsize] of byte, 0, nil); +} + +l2a[T](l: list of T): array of T +{ + a := array[len l] of T; + i := 0; + for(; l != nil; l = tl l) + a[i++] = hd l; + return a; +} + +insertsort[T](a: array of T) + for { T => cmp: fn(a, b: T): int; } +{ + for(i := 1; i < len a; i++) { + tmp := a[i]; + for(j := i; j > 0 && T.cmp(a[j-1], tmp) > 0; j--) + a[j] = a[j-1]; + a[j] = tmp; + } +} + +mflush(m: ref MSink, last: int): int +{ + d := array[len m.de] of byte; + + me := l2a(m.l); + insertsort(me); + o := Metablocksize; + deo := o+len m.l*Metaentrysize; + for(i := 0; i < len me; i++) { + me[i].me.offset += deo; + me[i].me.pack(d[o:]); + o += Metaentrysize; + } + d[o:] = m.de[:m.nde]; + o += m.nde; + if(!last) + while(o < len d) + d[o++] = byte 0; + + mb := Metablock.new(); + mb.nindex = len m.l; + mb.maxindex = mb.nindex; + mb.free = 0; + mb.size = o; + mb.pack(d); + + if(m.f.write(d[:o]) < 0) + return -1; + m.nde = 0; + m.l = nil; + return 0; +} + +MSink.add(m: self ref MSink, de: ref Direntry): int +{ + d := de.pack(); + if(d == nil) + return -1; +say(sprint("msink: adding direntry, length %d", len d)); + if(Metablocksize+len m.l*Metaentrysize+m.nde + Metaentrysize+len d > len m.de) + if(mflush(m, 0) < 0) + return -1; + m.de[m.nde:] = d; + m.l = ref Mentry(de.elem, ref Metaentry(m.nde, len d))::m.l; + m.nde += len d; + return 0; +} + +MSink.finish(m: self ref MSink): ref Entry +{ + if(m.nde > 0) + mflush(m, 1); + return m.f.finish(); +} + +Source.new(s: ref Session, e: ref Entry): ref Source +{ + return ref Source(s, e); +} + +power(b, e: int): big +{ + r := big 1; + while(e-- > 0) + r *= big b; + return r; +} + +blocksize(e: ref Entry): int +{ + if(e.psize > e.dsize) + return e.psize; + return e.dsize; +} + +Source.get(s: self ref Source, i: big, d: array of byte): int +{ + npages := (s.e.size+big (s.e.dsize-1))/big s.e.dsize; + if(i*big s.e.dsize >= s.e.size) + return 0; + + want := s.e.dsize; + if(i == npages-big 1) + want = int (s.e.size - i*big s.e.dsize); + last := s.e.score; + bsize := blocksize(s.e); + buf: array of byte; + + npp := s.e.psize/Scoresize; # scores per pointer block + np := power(npp, s.e.depth-1); # blocks referenced by score at this depth + for(depth := s.e.depth; depth >= 0; depth--) { + dtype := Pointertype0+depth-1; + if(depth == 0) { + dtype = Datatype; + if(s.e.flags & Entrydir) + dtype = Dirtype; + bsize = want; + } + buf = s.session.read(last, dtype, bsize); + if(buf == nil) + return -1; + if(depth > 0) { + pi := int (i / np); + i %= np; + np /= big npp; + o := (pi+1)*Scoresize; + if(o <= len buf) + last = Score(buf[o-Scoresize:o]); + else + last = Score.zero(); + } + } + for(j := len buf; j < want; j++) + d[j] = byte 0; + d[:] = buf; + return want; +} + + +Vacfile.mk(s: ref Source): ref Vacfile +{ + return ref Vacfile(s, big 0); +} + +Vacfile.new(s: ref Session, e: ref Entry): ref Vacfile +{ + return Vacfile.mk(Source.new(s, e)); +} + +Vacfile.seek(v: self ref Vacfile, offset: big): big +{ + v.o += offset; + if(v.o > v.s.e.size) + v.o = v.s.e.size; + return v.o; +} + +Vacfile.read(v: self ref Vacfile, d: array of byte, n: int): int +{ + have := v.pread(d, n, v.o); + if(have > 0) + v.o += big have; + return have; +} + +Vacfile.pread(v: self ref Vacfile, d: array of byte, n: int, offset: big): int +{ + dsize := v.s.e.dsize; + have := v.s.get(big (offset/big dsize), buf := array[dsize] of byte); + if(have <= 0) + return have; +say(sprint("vacfile.read: have=%d dsize=%d", have, dsize)); + o := int (offset % big dsize); + have -= o; + if(have > n) + have = n; + if(have <= 0) + return 0; + d[:] = buf[o:o+have]; + return have; +} + + +Vacdir.mk(vf: ref Vacfile, ms: ref Source): ref Vacdir +{ + return ref Vacdir(vf, ms, big 0, 0); +} + +Vacdir.new(session: ref Session, e, me: ref Entry): ref Vacdir +{ + vf := Vacfile.new(session, e); + ms := Source.new(session, me); + return Vacdir.mk(vf, ms); + +} + +mecmp(d: array of byte, i: int, elem: string, fromfossil: int): (int, int) +{ + me := Metaentry.unpack(d, i); + if(me == nil) + return (0, 1); + o := me.offset+6; + n := g16(d, o); + o += BIT16SZ; + if(o+n > len d) { + werrstr("bad elem in direntry"); + return (0, 1); + } + return (elemcmp(d[o:o+n], array of byte elem, fromfossil), 0); +} + +finddirentry(d: array of byte, elem: string): (int, ref Direntry) +{ + mb := Metablock.unpack(d); + if(mb == nil) + return (-1, nil); + fromfossil := g32(d, 0) == Metablockmagic+1; + + left := 0; + right := mb.nindex; + while(left+1 != right) { + mid := (left+right)/2; + (c, err) := mecmp(d, mid, elem, fromfossil); + if(err) + return (-1, nil); + if(c <= 0) + left = mid; + else + right = mid; + if(c == 0) + break; + } + de := readdirentry(d, left); + if(de != nil && de.elem == elem) + return (1, de); + return (0, nil); +} + +Vacdir.walk(v: self ref Vacdir, elem: string): ref Direntry +{ + i := big 0; + for(;;) { + n := v.ms.get(i, buf := array[v.ms.e.dsize] of byte); + if(n < 0) + return nil; + if(n == 0) + break; + (ok, de) := finddirentry(buf[:n], elem); + if(ok < 0) + return nil; + if(de != nil) + return de; + i++; + } + werrstr(sprint("no such file or directory")); + return nil; +} + +vfreadentry(vf: ref Vacfile, entry: int): ref Entry +{ +say(sprint("vfreadentry: reading entry=%d", entry)); + ebuf := array[Entrysize] of byte; + n := vf.pread(ebuf, len ebuf, big entry*big Entrysize); + if(n < 0) + return nil; + if(n != len ebuf) { + werrstr(sprint("bad archive, entry=%d not present", entry)); + return nil; + } + e := Entry.unpack(ebuf); + if(~e.flags&Entryactive) { + werrstr("entry not active"); + return nil; + } + if(e.flags&Entrylocal) { + werrstr("entry is local"); + return nil; + } +say(sprint("vreadentry: have entry, score=%s", e.score.text())); + return e; +} + +Vacdir.open(vd: self ref Vacdir, de: ref Direntry): (ref Entry, ref Entry) +{ +say(sprint("vacdir.open: opening entry=%d", de.entry)); + e := vfreadentry(vd.vf, de.entry); + if(e == nil) + return (nil, nil); + isdir1 := de.mode & Modedir; + isdir2 := e.flags & Entrydir; + if(isdir1 && !isdir2 || !isdir1 && isdir2) { + werrstr("direntry directory bit does not match entry directory bit"); + return (nil, nil); + } +say(sprint("vacdir.open: have entry, score=%s size=%bd", e.score.text(), e.size)); + me: ref Entry; + if(de.mode&Modedir) { + me = vfreadentry(vd.vf, de.mentry); + if(me == nil) + return (nil, nil); +say(sprint("vacdir.open: have mentry, score=%s size=%bd", me.score.text(), e.size)); + } + return (e, me); +} + +readdirentry(buf: array of byte, i: int): ref Direntry +{ + me := Metaentry.unpack(buf, i); + if(me == nil) + return nil; + o := me.offset; + de := Direntry.unpack(buf[o:o+me.size]); + if(badelem(de.elem)) { + werrstr(sprint("bad direntry: %s", de.elem)); + return nil; + } + return de; +} + +has(c: int, s: string): int +{ + for(i := 0; i < len s; i++) + if(s[i] == c) + return 1; + return 0; +} + +badelem(elem: string): int +{ + return elem == "" || elem == "." || elem == ".." || has('/', elem) || has(0, elem); +} + +Vacdir.readdir(vd: self ref Vacdir): (int, ref Direntry) +{ +say(sprint("vacdir.readdir: ms.e.size=%bd vd.p=%bd vd.i=%d", vd.ms.e.size, vd.p, vd.i)); + dsize := vd.ms.e.dsize; + n := vd.ms.get(vd.p, buf := array[dsize] of byte); + if(n <= 0) + return (n, nil); +say(sprint("vacdir.readdir: have buf, length=%d e.size=%bd", n, vd.ms.e.size)); + mb := Metablock.unpack(buf); + if(mb == nil) + return (-1, nil); + de := readdirentry(buf, vd.i); + if(de == nil) + return (-1, nil); + vd.i++; + if(vd.i >= mb.nindex) { + vd.p++; + vd.i = 0; + } +say("vacdir.readdir: have entry"); + return (1, de); +} + +Vacdir.rewind(vd: self ref Vacdir) +{ + vd.p = big 0; + vd.i = 0; +} + + +vdroot(session: ref Session, score: Venti->Score): (ref Vacdir, ref Direntry, string) +{ + d := session.read(score, Roottype, Rootsize); + if(d == nil) + return (nil, nil, sprint("reading vac score: %r")); + r := Root.unpack(d); + if(r == nil) + return (nil, nil, sprint("bad vac root block: %r")); + say("have root"); + topscore := r.score; + + d = session.read(topscore, Dirtype, 3*Entrysize); + if(d == nil) + return (nil, nil, sprint("reading rootdir score: %r")); + if(len d != 3*Entrysize) { + say("top entries not in directory of 3 elements, assuming it's from fossil"); + if(len d % Entrysize != 0 && len d == 2*Entrysize != 0) # what's in the second 40 bytes? looks like 2nd 20 bytes of it is zero score + return (nil, nil, sprint("bad fossil rootdir, have %d bytes, need %d or %d", len d, Entrysize, 2*Entrysize)); + e := Entry.unpack(d[:Entrysize]); + if(e == nil) + return (nil, nil, sprint("unpacking fossil top-level entry: %r")); + topscore = e.score; + d = session.read(topscore, Dirtype, 3*Entrysize); + if(d == nil) + return (nil, nil, sprint("reading fossil rootdir block: %r")); + say("have fossil top entries"); + } + say("have top entries"); + + e := array[3] of ref Entry; + j := 0; + for(i := 0; i+Entrysize <= len d; i += Entrysize) { + e[j] = Entry.unpack(d[i:i+Entrysize]); + if(e[j] == nil) + return (nil, nil, sprint("reading root entry %d: %r", j)); + j++; + } + say("top entries unpacked"); + + mroot := Vacdir.new(session, nil, e[2]); + (ok, de) := mroot.readdir(); + if(ok <= 0) + return (nil, nil, sprint("reading root meta entry: %r")); + +say(sprint("vdroot: new score=%s", score.text())); + return (Vacdir.new(session, e[0], e[1]), de, nil); +} + + +checksize(n: int): int +{ + if(n < 256 || n > Venti->Maxlumpsize) { + sys->werrstr("bad block size"); + return 0; + } + return 1; +} + +gscore(f: array of byte, i: int): Score +{ + s := Score(array[Scoresize] of byte); + s.a[0:] = f[i:i+Scoresize]; + return s; +} + +g16(f: array of byte, i: int): int +{ + return (int f[i] << 8) | int f[i+1]; +} + +g32(f: array of byte, i: int): int +{ + return (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3]; +} + +g48(f: array of byte, i: int): big +{ + b1 := (((((int f[i+0] << 8) | int f[i+1]) << 8) | int f[i+2]) << 8) | int f[i+3]; + b0 := (int f[i+4] << 8) | int f[i+5]; + return (big b1 << 16) | big b0; +} + +g64(f: array of byte, i: int): big +{ + b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; + b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4]; + return (big b1 << 32) | (big b0 & 16rFFFFFFFF); +} + +p16(d: array of byte, i: int, v: int): int +{ + d[i+0] = byte (v>>8); + d[i+1] = byte v; + return i+BIT16SZ; +} + +p32(d: array of byte, i: int, v: int): int +{ + p16(d, i+0, v>>16); + p16(d, i+2, v); + return i+BIT32SZ; +} + +p48(d: array of byte, i: int, v: big): int +{ + p16(d, i+0, int (v>>32)); + p32(d, i+2, int v); + return i+BIT48SZ; +} + +p64(d: array of byte, i: int, v: big): int +{ + p32(d, i+0, int (v>>32)); + p32(d, i+0, int v); + return i+BIT64SZ; +} + +ptstring(d: array of byte, i: int, s: string, l: int): int +{ + a := array of byte s; + if(len a > l) { + sys->werrstr("string too long: "+s); + return -1; + } + for(j := 0; j < len a; j++) + d[i+j] = a[j]; + while(j < l) + d[i+j++] = byte 0; + return i+l; +} + +pscore(d: array of byte, i: int, s: Score): int +{ + for(j := 0; j < Scoresize; j++) + d[i+j] = s.a[j]; + return i+Scoresize; +} + +echeck(f: array of byte, i: int, l: int) +{ + if(i+l > len f) + raise sprint("too small: buffer length is %d, requested %d bytes starting at offset %d", len f, l, i); +} + +egscore(f: array of byte, i: int): (Score, int) +{ + echeck(f, i, Scoresize); + return (gscore(f, i), i+Scoresize); +} + +egstring(a: array of byte, o: int): (string, int) +{ + (s, no) := gstring(a, o); + if(no == -1) + raise sprint("too small: string runs outside buffer (length %d)", len a); + return (s, no); +} + +eg16(f: array of byte, i: int): (int, int) +{ + echeck(f, i, BIT16SZ); + return (g16(f, i), i+BIT16SZ); +} + +eg32(f: array of byte, i: int): (int, int) +{ + echeck(f, i, BIT32SZ); + return (g32(f, i), i+BIT32SZ); +} + +eg48(f: array of byte, i: int): (big, int) +{ + echeck(f, i, BIT48SZ); + return (g48(f, i), i+BIT48SZ); +} + +eg64(f: array of byte, i: int): (big, int) +{ + echeck(f, i, BIT64SZ); + return (g64(f, i), i+BIT64SZ); +} + +say(s: string) +{ + if(dflag) + fprint(fildes(2), "%s\n", s); +} diff --git a/dis/lib/vac.dis b/dis/lib/vac.dis Binary files differnew file mode 100644 index 00000000..7c7d558c --- /dev/null +++ b/dis/lib/vac.dis diff --git a/dis/vacfs.dis b/dis/vacfs.dis Binary files differnew file mode 100644 index 00000000..45c1da64 --- /dev/null +++ b/dis/vacfs.dis diff --git a/dis/vacget.dis b/dis/vacget.dis Binary files differnew file mode 100644 index 00000000..c68f1c98 --- /dev/null +++ b/dis/vacget.dis diff --git a/dis/vacput.dis b/dis/vacput.dis Binary files differnew file mode 100644 index 00000000..2b45397c --- /dev/null +++ b/dis/vacput.dis diff --git a/lib/proto/inferno b/lib/proto/inferno index 00cbf765..93c252b9 100644 --- a/lib/proto/inferno +++ b/lib/proto/inferno @@ -397,6 +397,7 @@ appl broke.b bytes.b cal.b + calc.b cat.b cd.b cddb.b @@ -543,7 +544,6 @@ appl ppptest.b script.b script.m - obootpd.b ping.b ppp mkfile @@ -565,7 +565,6 @@ appl itest.b itreplay.b kill.b - lc.b lego clock.b clockface.b @@ -632,7 +631,6 @@ appl symb.b tk.b xeq.b - mathcalc.b mc.b md5sum.b mdb.b @@ -756,6 +754,9 @@ appl mkfile uuencode.b uudecode.b + vacfs.b + vacget.b + vacput.b wav2iaf.b wc.b webgrab.b @@ -925,6 +926,7 @@ appl ibm866.b iso8859-1.b iso8859-10.b + iso8859-15.b iso8859-2.b iso8859-3.b iso8859-4.b @@ -1118,6 +1120,7 @@ appl usbmct.b usbmouse.b utils.m + vac.b venti.b virgil.b volume.b @@ -1480,6 +1483,7 @@ dis broke.dis bytes.dis cal.dis + calc.dis cat.dis cd.dis cddb.dis @@ -1552,7 +1556,7 @@ dis itest.dis itreplay.dis kill.dis - lc.dis + lc lego + lib @@ -1709,6 +1713,7 @@ dis usbmass.dis usbmct.dis usbmouse.dis + vac.dis venti.dis virgil.dis volume.dis @@ -1737,7 +1742,6 @@ dis mash.dis math + - mathcalc.dis mc.dis md5sum.dis mdb.dis @@ -1831,6 +1835,9 @@ dis usbd.dis uudecode.dis uuencode.dis + vacfs.dis + vacget.dis + vacput.dis wav2iaf.dis wc.dis webgrab.dis @@ -1963,6 +1970,7 @@ doc limbotk tk.ms tk.pdf + manhow.pdf mk.ms mk.pdf perform @@ -2005,7 +2013,6 @@ fonts psrename icons + -install keydb countersigned keys 600 inferno inferno /keydb/keys.dist @@ -2069,6 +2076,7 @@ lib + units usbdb + video.specs wmcharon wmsetup wmsetup.grid @@ -2261,7 +2269,7 @@ module libc.m libc0.m linalg.m - lists.mappl + lists.m loader.m lock.m man.m @@ -2333,6 +2341,7 @@ module uris.m url.m usb.m + vac.m venti.m volume.m wait.m @@ -2376,6 +2385,8 @@ services inferno.gif start.html vnlogo.gif +src +sys #mux # basic # email diff --git a/lib/proto/src b/lib/proto/src index 4779b1af..7b8c5241 100644 --- a/lib/proto/src +++ b/lib/proto/src @@ -1,3 +1,4 @@ +INSTALL asm NOTICE asm.h @@ -769,6 +770,19 @@ libtk utils.c windw.c xdata.c +man + lib + checkman.awk + colophon + lookman + + + man + notes + permind + preface + secindex + title + trademarks tools NOTICE db diff --git a/man/1/INDEX b/man/1/INDEX index 3d907507..159a36d9 100644 --- a/man/1/INDEX +++ b/man/1/INDEX @@ -283,6 +283,8 @@ uniq uniq units units uudecode uuencode uuencode uuencode +vacget vacget +vacput vacget wc wc webgrab webgrab wish wish diff --git a/man/1/vacget b/man/1/vacget new file mode 100644 index 00000000..8a5b04f9 --- /dev/null +++ b/man/1/vacget @@ -0,0 +1,75 @@ +.TH VACGET 1 +.SH NAME +vacget, vacput \- venti archive utilities +.SH SYNOPSIS +.B vacget +[ +.B -vdpt +] [ +.B -a +.I addr +] +.I vac:score +.br +.B vacput +[ +.B -vd +] [ +.B -a +.I addr +] [ +.B -b +.I blocksize +] [ +.B -n +.I name +] +.I path ... +.SH DESCRIPTION +.I Vacget +retrieves a venti archive from a venti server to the current working directory. +.PP +.I Vacput +writes a venti archive to a venti server. The +.I paths +are walked recursively and all files and directories written to the archive. Temporary files, i.e. those with the +.B DMTMP +bit set, are skipped. Writing only changed files relative to a previously written archive is not implemented. +.TP +.B -d +Print debug messages. +.TP +.B -p +Try to preserve file permissions and owner/group. Only for vacget. +.TP +.B -v +Be verbose. Prints files as they are being retrieved or written. +.TP +.B -t +List files, do not write them. Only for vacget. +.TP +.BI -a " address" +Dial +.I address +instead of the default venti server. +.TP +.BI -b " blocksize" +Use blocks with +.I blocksize +bytes instead of the default 8192 byte blocks. Only for vacput. +.TP +.BI -n " name" +Use +.I name +as the name in the root block. Only for vacput. +.SH SOURCE +.B /appl/cmd/vacget.b +.br +.B /appl/cmd/vacput.b +.SH SEE ALSO +.IR vcache (1), +.IR venti (2), +.IR vacfs (4), +.IR ventisrv (8) +.SH BUGS +These tools need more testing. diff --git a/man/4/INDEX b/man/4/INDEX index ce9e711e..69dcfb7c 100644 --- a/man/4/INDEX +++ b/man/4/INDEX @@ -29,3 +29,4 @@ regquery registry spree spree tarfs tarfs trfs trfs +vacfs vacfs diff --git a/man/4/vacfs b/man/4/vacfs new file mode 100644 index 00000000..1cfeaff2 --- /dev/null +++ b/man/4/vacfs @@ -0,0 +1,47 @@ +.TH VACFS 4 +.SH NAME +vacfs \- mount venti archive +.SH SYNOPSIS +.B vacfs +[ +.B -Ddp +] [ +.B -a +.I addr +] +.I [vac:score] +.SH DESCRIPTION +.I Vacfs +makes the contents of a venti archive available over styx. Standard input is used for reading and writing styx messages. +If +.I score +is not specified, vacfs +serves multiple venti archives. In this case the root directory lists no files, but walking to a directory +.I score +opens the venti archive with that score. Note that the vacfs does not support writing. +.TP +.B -D +Print styx trace messages. +.TP +.B -d +Print debug messages. +.TP +.B -p +Disable permission checking. +.TP +.BI -a " address" +Dial +.I address +instead of the default venti server. +.SH SOURCE +.B /appl/cmd/vacfs.b +.SH SEE ALSO +.IR vacget (1), +.IR vacput (1), +.IR vcache (1), +.IR venti (2), +.IR ventisrv (8) +.SH BUGS +Vacfs needs more testing. +.br +When the venti connection is broken, directories appear empty. diff --git a/man/lib/checkman.awk b/man/lib/checkman.awk new file mode 100644 index 00000000..ea17e8d6 --- /dev/null +++ b/man/lib/checkman.awk @@ -0,0 +1,187 @@ +# Usage: awk -f checkman.awk man?/*.? +# +# Checks: +# - .TH is first line, and has proper name section number +# - sections are in order NAME, SYNOPSIS, DESCRIPTION, EXAMPLES, +# FILES, SOURCE, SEE ALSO, DIAGNOSTICS, BUGS +# - there's a manual page for each cross-referenced page + +BEGIN { + +# .SH sections should come in the following order + + Weight["NAME"] = 1 + Weight["SYNOPSIS"] = 2 + Weight["DESCRIPTION"] = 4 + Weight["EXAMPLE"] = 8 + Weight["EXAMPLES"] = 16 + Weight["FILES"] = 32 + Weight["SOURCE"] = 64 + Weight["SEE ALSO"] = 128 + Weight["DIAGNOSTICS"] = 256 + Weight["SYSTEM CALLS"] = 512 + Weight["BUGS"] = 1024 +} + +FNR==1 { + n = length(FILENAME) + seclen = 0 + if (substr(FILENAME, 2, 1) == "/") + seclen = 1 + else if (substr(FILENAME, 3, 1) == "/") + seclen = 2 + if(seclen == 0) + print "FILENAME", FILENAME, "not of form [0-9][0-9]?/*" + else if(!(substr(FILENAME, seclen+2, n-seclen-1) ~ /^[A-Z]+$/)){ + section = substr(FILENAME, 1, seclen) + name = substr(FILENAME, seclen+2, n-seclen-1) + if($1 != ".TH" || NF != 3) + print "First line of", FILENAME, "not a proper .TH" + else if($2 != toupper(name) || substr($3, 1, seclen) != section){ + if($2!="INTRO" || name!="0intro") + print ".TH of", FILENAME, "doesn't match filename" + }else + Pages[section "/" $2] = 1 + } + Sh = 0 + } + +$1 == ".SH" { + if(inex) + print "Unterminated .EX in", FILENAME, ":", $0 + inex = 0; + if (substr($2, 1, 1) == "\"") { + if (NF == 2) { + print "Unneeded quote in", FILENAME, ":", $0 + $2 = substr($2, 2, length($2)-2) + } else if (NF == 3) { + $2 = substr($2, 2) substr($3, 1, length($3)-1) + NF = 2 + } + } + w = Weight[$2] + if (w) { + if (w < Sh) + print "Heading", $2, "out of order in", FILENAME + Sh += w + } +} + +$1 == ".EX" { + if(inex) + print "Nested .EX in", FILENAME, ":", $0 + inex = 1 +} + +$1 == ".EE" { + if(!inex) + print "Bad .EE in", FILENAME, ":", $0 + inex = 0; +} + +$0 ~ /^\..*\([0-9]\)/ { + if ($1 == ".IR" && $3 ~ /\([0-9]\)/) { + name = $2 + section = $3 + }else if ($1 == ".RI" && $2 == "(" && $4 ~ /\([0-9]\)/) { + name = $3 + section = $4 + }else if ($1 == ".IR" && $3 ~ /9.\([0-9]\)/) { + name = $2 + section = "9" + }else if ($1 == ".RI" && $2 == "(" && $4 ~ /9.\([0-9]\)/) { + name = $3 + section = "9" + } else { + print "Possible bad cross-reference format in", FILENAME + print $0 + next + } + gsub(/[^0-9]/, "", section) + Refs[section "/" toupper(name)]++ +} + +END { + print "Checking Cross-Referenced Pages" + for (i in Refs) { + if (!(i in Pages)) + print "Need", tolower(i) + } + print "" + print "Checking commands" + getindex("/usr/inferno/doc/man/1") + getindex("/usr/inferno/doc/man/8") + Skipdirs["lib"] = 1 + getbinlist("/usr/inferno/dis") + for (i in List) { + if (!(i in Index)) + print "Need", i, "(in " List[i] ")" + } + clearindex() + clearlist() + print "" + print "Checking libraries" + getindex("/usr/inferno/doc/man/2") + getbinlist("/usr/inferno/dis/lib") + for (i in List) { + if (!(i in Index)) + print "Need", i, "(in " List[i] ")" + } +} + +func getindex(dir, fname) +{ + fname = dir "/INDEX" + while ((getline < fname) > 0) + Index[$1] = dir + close(fname) +} + +func getindex9(dir, fname) +{ + fname = dir "/INDEX" + while ((getline < fname) > 0) + if($2 ~ "(getflags|picopen|getcmap)") + Index[$1] = dir + close(fname) +} + +func getbinlist(dir, cmd, subdirs, nsd) +{ + cmd = "ls -p -l " dir + nsd = 0 + while (cmd | getline) { + if ($1 ~ /^d/) { + if (!($10 in Skipdirs)) + subdirs[++nsd] = $10 + } else { + sub(".dis", "", $10) + List[$10] = dir + } + } + for ( ; nsd > 0 ; nsd--) + getbinlist(dir "/" subdirs[nsd]) + close(cmd) +} + +func getnmlist(lib, cmd) +{ + cmd = "nm -g -h " lib + while (cmd | getline) { + if (($1 == "T" || $1 == "L") && $2 !~ "^_") + List[$2] = lib + } + close(cmd) +} + +func clearindex( i) +{ + for (i in Index) + delete Index[i] +} + +func clearlist( i) +{ + for (i in List) + delete List[i] +} diff --git a/man/lib/colophon b/man/lib/colophon new file mode 100644 index 00000000..ac7180e8 --- /dev/null +++ b/man/lib/colophon @@ -0,0 +1,26 @@ +.sp |3i +.vs 12 +.ll 5.1i +.in |0.4i +.fp 1 R LucidaSans +.ft 1 +.fi +This book was typeset by the authors using +.sp +.ft CW +.ps -1 +.ce +eqn | tbl | troff -mpm | lp -d stdout | cropmarks +.ps +1 +.ft R +.sp +The input text was characters from the Unicode Standard encoded in UTF-8. +.sp +The fonts used were Lucida Sans, +in a special version incorporating over 1700 characters from the Unicode Standard, +along with Lucida Sans Italic, +Lucida Sans DemiBold, and +Lucida Typewriter, +designed by Bigelow & Holmes, Atherton, California. +The hinted Adobe Type 1 representation of the fonts was +provided by Y&Y Inc., 45 Walden Street, Concord, MA, 01742, USA. diff --git a/man/lib/lookman/junkwords b/man/lib/lookman/junkwords new file mode 100644 index 00000000..a0e8c6d4 --- /dev/null +++ b/man/lib/lookman/junkwords @@ -0,0 +1,509 @@ + +a +about +above +according +across +act +action +after +again +against +ago +air +all +allowed +almost +along +already +also +although +always +american +among +an +and +another +any +anything +appear +appears +appropriate +are +area +areas +argument +arguments +around +as +asked +associated +at +available +away +b +back +be +became +because +become +been +before +began +begin +beginning +begins +behind +being +below +best +better +between +big +board +body +both +boy +brought +business +but +by +c +call +called +calls +came +can +cannot +car +case +cause +causes +certain +change +changed +changes +changing +children +church +city +close +college +come +command +commands +community +company +contain +containing +contents +corresponding +could +country +course +court +currently +d +day +days +death +depending +description +development +did +didnt +different +do +does +doesnt +done +dont +door +down +during +e +each +early +economic +effect +either +element +elements +else +empty +encountered +end +enough +even +ever +every +example +examples +experience +eyes +f +face +fact +family +far +federal +feet +felt +few +field +find +first +five +followed +following +follows +for +force +form +found +four +free +from +function +functions +g +gave +general +get +give +given +gives +giving +go +god +going +good +got +government +great +group +h +had +half +hand +hands +has +have +having +he +head +heard +held +help +her +here +high +him +himself +his +history +home +house +how +however +human +i +if +ignored +im +immediately +implemented +important +in +including +individual +information +initial +instead +interest +interpreted +into +is +it +its +itself +j +job +john +just +k +keep +kind +knew +know +known +l +large +last +later +law +least +left +less +let +life +light +like +line +little +local +long +look +looked +love +m +made +major +make +makes +making +man +many +marked +matter +may +me +meaning +means +members +men +might +mind +miss +moment +money +more +most +mr +mrs +much +multiple +must +my +n +name +named +national +need +never +new +next +night +no +nonzero +normal +normally +not +nothing +now +number +numbers +o +of +off +office +often +old +on +once +one +only +open +or +order +other +others +otherwise +our +out +over +own +p +part +passed +past +people +per +perhaps +period +place +placed +point +political +position +possible +power +preceded +present +president +previous +probably +problem +problems +program +public +put +q +question +quite +r +rather +really +reason +represented +respectively +result +right +room +s +said +same +saw +say +school +second +see +seemed +seems +seen +sense +separate +separated +sequence +service +set +sets +several +shall +she +should +show +side +simple +since +single +small +so +social +society +some +something +south +special +specified +start +starting +state +states +still +street +study +subsequent +such +supplied +sure +synopsis +system +t +take +taken +tell +terminated +terminating +than +that +the +their +them +themselves +then +there +these +they +thing +things +think +this +those +though +thought +three +through +thus +to +today +together +told +too +took +top +toward +trailing +treated +true +turn +turned +two +u +under +united +until +up +upon +us +use +used +useful +uses +usually +v +very +w +want +war +was +water +way +we +week +well +went +were +west +what +when +where +whether +which +while +white +who +whole +whose +why +will +with +within +without +word +words +work +world +would +x +y +year +years +yet +york +you +young +your +z +zero +zeros diff --git a/man/lib/lookman/mkindex b/man/lib/lookman/mkindex new file mode 100755 index 00000000..45c48d74 --- /dev/null +++ b/man/lib/lookman/mkindex @@ -0,0 +1,13 @@ +#!/bin/rc +# creates the index used by lookman +>index +for(i in /usr/inferno/man/[0-9]*/[a-z0-9:]*){ + p=`{echo $i | sed 's@/usr/inferno(/man/.*)$@\1@'} + deroff -w < $i | + tr 'A-Z' 'a-z' | + sort -u | + comm -23 - junkwords | + sed 's@$@ '$p'@' >>index # stick file name on end of line +} +sort -o index index +mv index /usr/inferno/man/index diff --git a/man/lib/notes b/man/lib/notes new file mode 100644 index 00000000..5ea602d9 --- /dev/null +++ b/man/lib/notes @@ -0,0 +1,10 @@ +.fp 1 R LucidaSans +.ft 1 +.ps 20 +.sp |0.5i +.ce +Notes +.bp +.sp |0.5i +.ce +Notes diff --git a/man/lib/preface b/man/lib/preface new file mode 100644 index 00000000..a49bc721 --- /dev/null +++ b/man/lib/preface @@ -0,0 +1,73 @@ +.FP lucidasans +.nr PS -1 +.nr VS -1 +.TL +Preface +.SP 0.4i exactly +.LP +Inferno benefits from the results of many years of systems research +at the Computing Science Research Center at Lucent Technologies, Bell Labs. +The system is clearly a cultural descendent of the earliest Unix systems, +and amongst Inferno's inventors, listed below, are several venerable programmers +associated with the development of Unix. +Inferno looks out on a very different world from Unix: complexity is no longer +confined to large mainframes, but has sprawled +across world wide networks, trapping programmers in its web. +.LP +Inferno tackles this as radically now as Unix did then. +First, it adopts key ideas from the system Plan 9, also from Bell Labs: +.IP \(bu +Replace a plethora of protocols by a simple, unifying file service protocol (Styx), +that can be served even by tiny devices, giving a uniform +way to access objects throughout the network. +.IP \(bu +Let applications `compute a name space': all resources are represented +as file systems, which an application assembles into an application-specific +hierarchy or `name space', private or shared, that hides their source (local or remote) +and nature (static or dynamic), for completely transparent access. +.IP \(bu +Using those primitives, implement windowing systems, networked graphics, remote debugging, +device control, and much more, with remarkable ease +and great simplicity. +.LP +Inferno carries Plan 9's ideas further. +Plan 9 virtualised resources; Inferno virtualises the whole system. +The operating system kernel can run both native and `hosted' on a range +of platforms presenting identical interfaces on all, offering wider portability. +The Limbo programming language offers proper concurrent programming, +and straightforward yet dynamic modularity. +The Dis virtual machine allows applications to cross architecture boundaries +invisibly during execution. +Inferno shows the `continued appliance of computer science'. +.LP +The original development team at Bell Labs was +Sean Dorward, Rob Pike and Phil Winterbottom, +with Eric Grosse, Jim McKie, Dave Presotto, +Dennis Ritchie, Ken Thompson and Howard Trickey. +Many others have contributed much since then, both within Lucent and without. +.LP +Inferno® is now a supported, commercial product of Vita Nuova. +The Third Edition of the Programmer's manual marked that event. +The Fourth Edition brings many changes in content, but also makes the full +source available as Free Software under a new `dual licence' scheme. +.LP +.sp +.in 4i +.nf +.ft I +.ce 100 +Dave Atkin +John Bates +Danny Byrne +John Firth +Charles Forsyth +Michael Jeffrey +Chris Locke +Roger Peppé +Nigel Roles +.sp +Vita Nuova +.br +June 2003 +.ce 0 +.in -4i diff --git a/man/lib/secindex b/man/lib/secindex new file mode 100755 index 00000000..060e7a6a --- /dev/null +++ b/man/lib/secindex @@ -0,0 +1,43 @@ +#!/bin/rc +U='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +L='abcdefghijklmnopqrstuvwxyz' +builtin cd $1 +for (i in [a-z0-9:]*) { + sed -n ' + /SH *NAM/,/SH/{ + /SH/d + s/, *$// + ty + :y + s/ *\\*-.*// + tx + s/ *\\\(mi.*// + tx + s/[,:] */\ +/g + s/\n\\n/\ +/g + y/'$U'/'$L'/ + s/\n/ '$i'&/g + s/$/ '$i'/ + p + } + /SH *DES/q + d + :x + s/ *\\*-.*// + s/ *\\\(mi.*// + /^$/d + s/[,:] */\ +/g + s/\n\n/\ +/g + y/'$U'/'$L'/ + s/\n/ '$i'&/g + s/(.|\n)*$/& '$i'/ + p + q +' $i +kw=`{echo $i | sed 's/0intro/intro/'} +echo $kw $i +} | sort -u diff --git a/man/lib/title b/man/lib/title new file mode 100644 index 00000000..f9b2f6ec --- /dev/null +++ b/man/lib/title @@ -0,0 +1,77 @@ +.fp 1 R LucidaSans +.fp 2 I LucidaSansI +.ps36 +.sp |2i +.ce +Inferno\s36\u\s8\u™ +.sp |3.275i +.ps24 +.ce +Programmer's Manual +.sp .3i +.ce +Volume 1 +.ps12 +.sp |4.8i +.ft I +.ce +Fourth Edition +.sp |7.6i +.ce +Vita Nuova Holdings Limited +.ce +3 Innovation Close +.ce +York Science Park +.ce +University Road +.ce +York YO10 5ZF +.ce +England +.bp +.ft R +\" .ll 5.5i +\" .in .5i +.hy 0 +.vs 10p +.ps 10 +.sp |1i +.nf +Published by Vita Nuova Holdings Limited, +3 Innovation Close, +York Science Park, +University Road, +York YO10 5ZF, +England +.sp 3 +.ps 8 +Copyright © 1995-1999 Lucent Technologies Inc. All Rights Reserved. +Portions Copyright © 1999-2005 Vita Nuova Holdings Limited. All Rights Reserved. +Portions of Section 9 are derived from works which themselves are +Copyright © 1990 The Regents of the University of California, and +Copyright © 1994-1996 Sun Microsystems, Inc. +subject to the terms described in the copyright notice in Section 9. +.ps 10 +.sp 3 +All rights reserved; no part of this publication may be reproduced, +stored in a retrieval system or transmitted in any form or by any means, +electronic, mechanical, photocopying, recording or otherwise without +the prior written permission of the publishers. +.sp 3 +First published 2000. This edition published 2005. +.sp 3 +.ig +ISBN for complete set of 2 volumes: 0 9538701 0 3 +.br +ISBN for this volume: 0 9538701 1 1 +.sp 3 +.ft I +Printed in Great Britain by +William Clowes, +Beccles, +Suffolk NR34 9QE, +England. +.ft R +.sp 3 +Cover Design: Jeff Parker diff --git a/man/lib/trademarks b/man/lib/trademarks new file mode 100644 index 00000000..efa19bb0 --- /dev/null +++ b/man/lib/trademarks @@ -0,0 +1,46 @@ +.sp |2i +.vs 12 +.fp 1 R LucidaSans +.ft 1 +.fi +Trademarks referenced in this document: +.sp 2 +.ps -1 +.vs -1 +.nf +Inferno, Dis, Styx and Limbo are registered trademarks of Vita Nuova Holdings Limited + in the United States and other countries. +68020 and 68040 are trademarks of Motorola. +AMD is a trade mark of Advanced Micro Devices. +ARM is a trade mark of Advanced Risc Machines, Limited. +DSP3210 is a trade mark of AT&T. +Aladdin Ghostscript is a trademark of Aladdin Enterprises. +CGA is a trademark of International Business Machines Corporation. +Challenge, Indigo\s-2\u2\d\s0, Indy, and POWER Series are trademarks of Silicon Graphics, Inc. +Ethernet is a trademark of Xerox Corporation. +IBM, PS/2, and VGA are registered trademarks of International Business Machines Corporation. +IDEA is a trademark of Ascom-Tech AG. +Intel, i386, 960, 8088, 80286, 80386, 80486, and Pentium are trademarks of Intel Corporation. +Java is a trademark and Sun is a registered trademark of Sun Microsystems Inc. +Lucida and Pellucida are registered trademarks of Bigelow & Holmes. +MIPS and R3000 are registered trademarks of MIPS Technologies, Inc. +Microsoft is a registered trademark of Microsoft Corporation. +Windows, Windows NT and MS-DOS are registered trademarks of Microsoft Corporation + in the United States and other countries. +NFS is a registered trademark of Sun Microsystems, Inc. +PDP and VAX are registered trademarks of Digital Equipment Corp. +Plan 9 is a trademark of Lucent Technologies Inc. +PostScript is a registered trademark of Adobe Systems Incorporated. +ProPhone is a registered trademark of Prolink Corporation. +R2000, R6000, R4000, and R4400 are trademarks of MIPS Technologies, Inc. +RC4 is a registered trademark of RSA Security Inc. +SecureNet is a registered trademark of Digital Pathways, Inc. +Silicon Graphics, IRIS Indigo, IRIS, and IRIX are registered trademarks of Silicon Graphics, Inc. +Sound Blaster is a registered trademark of Creative Labs, Inc. +SPARC is a registered trademark of SPARC International, Inc. +StrongARM is a registered trademark of Advanced RISC Machines, Ltd. +ThinkJet is a registered trademark of the Hewlett-Packard Company. +Unicode is a registered trademark of Unicode, Inc. +UNIX is a registered trademark of The Open Group. +.ps +.vs diff --git a/man/mkfile b/man/mkfile new file mode 100644 index 00000000..336c94ab --- /dev/null +++ b/man/mkfile @@ -0,0 +1,119 @@ +<../mkconfig +DOC=$ROOT +<$DOC/man/fonts + +LIB=$DOC/man/lib + +default:V: check + +indices:V: + rfork n + for (i in [0-9] 10){ + $LIB/secindex $i > $i/INDEX + } + mk lookindex + + +permind:V: + rm -f $LIB/permind/toc + { + echo -n $FONTS + echo .am TH + echo .tm '\\$1' '\\$2' '\\n%' + echo .. + for (i in [0-9]){ + builtin cd $i + for(j in [a-z0-9]*) + switch($i/$j){ + case 1/tbl + tbl $j + case 1/eqn 6/auth + eqn $j + case 1/pic + pic $j + case 1/grap + grap $j | pic + case * + cat $j + } + builtin cd .. + } + # section 10 is in a special order + builtin cd 10 + cat 0intro + cat `{grep -l '^\.TH.*\.1' *} + cat `{grep -l '^\.TH.*\.2' *} + cat `{grep -l '^\.TH.*\.6' *} + cat `{grep -l '^\.TH.*\.8' *} + builtin cd .. + } | troff -$MAN > /dev/null >[2] $LIB/permind/toc + builtin cd $LIB/permind + rm -f out + mk out > /dev/null >[2] /dev/null + +check:V: checksource + awk -f $LIB/checkman.awk [0-9]/* | sed '/\/(cda|av|midi|pub|weather|service\.9net|isdn)(\/|\))/d' + +checksource:QV: + sam -d >[2]/dev/null <<'!' + f input + < cat [0-9]/[0-9a-z]* + B output + b input + ,x/^\.SH SOURCE/ .,/^\.SH/ x g/^\.B/t "output + b output + ,x/^\.B.? / d + ,x/ .*/d + ,s/.+/if(! test -f $ROOT& \&\& ! test -d $ROOT&) echo no such SOURCE file '&'/g + ,>rc + ! + +lookindex:V: + builtin cd $LIB/lookman + mkindex + + +print.out:V: permind + { + {echo -n $FONTS; cat $LIB/title} | troff + {echo -n $FONTS; cat $LIB/trademarks} | troff -ms + {echo -n $FONTS; echo ' '} | troff + {echo -n $FONTS; cat $LIB/preface} | troff -ms + {echo -n $FONTS; echo ' '} | troff + { + for (i in [0-9]){ + builtin cd $i + for(j in [a-z0-9]*) + switch($i/$j){ + case 1/tbl + tbl $j + case 1/eqn 6/auth + eqn $j + case 1/pic + pic $j + case 1/grap + grap $j | pic + case * + cat $j + } + builtin cd .. + } + # section 10 is in a special order + builtin cd 10 + cat 0intro + cat `{grep -l '^\.TH.*\.1' *} + cat `{grep -l '^\.TH.*\.2' *} + cat `{grep -l '^\.TH.*\.6' *} + cat `{grep -l '^\.TH.*\.8' *} + builtin cd .. + } | troff -$MAN + {echo -n $FONTS; echo ' '} | troff + {echo -n $FONTS; echo ' '} | troff + cat $LIB/permind/out + {echo -n $FONTS; echo ' '} | troff + {echo -n $FONTS; echo ' '} | troff + {echo -n $FONTS; cat $LIB/colophon} | troff + } > print.out + +clean:V: + rm -f man.out diff --git a/module/vac.m b/module/vac.m new file mode 100644 index 00000000..fd6be340 --- /dev/null +++ b/module/vac.m @@ -0,0 +1,188 @@ +Vac: module { + PATH: con "/dis/lib/vac.dis"; + init: fn(); + + dflag: int; + + # taken from venti.m, merge back later + # some of this needs to be removed from venti.m since it does not belong there + + # mode bits + Modeperm: con 8r777; + Modesticky, + Modesetuid, + Modesetgid, + Modeappend, + Modeexcl, + Modesymlink, + Modedir, + Modehidden, + Modesystem, + Modearchive, + Modetemp, + Modesnapshot, + Modedev, + Modenamedpipe: con 1<<(9+iota); + + Entrysize: con 40; + Rootsize: con 300; + Metablocksize: con 12; + Metaentrysize: con 4; + + Dsize: con 8*1024; + + Entryactive: con (1<<0); # entry is in use + Entrydir: con (1<<1); # a directory + Entrydepthshift: con 2; # shift for pointer depth + Entrydepthmask: con (16r7<<2); # mask for pointer depth + Entrylocal: con (1<<5); # used for local storage: should not be set for venti blocks + + Root: adt { + version: int; + name: string; + rtype: string; + score: Venti->Score; # to a Dir block + blocksize: int; # maximum block size + prev: ref Venti->Score; # last root block + + new: fn(name, rtype: string, score: Venti->Score, blocksize: int, prev: ref Venti->Score): ref Root; + unpack: fn(d: array of byte): ref Root; + pack: fn(r: self ref Root): array of byte; + }; + + Entry: adt { + gen: int; # generation number (XXX should be unsigned) + psize: int; # pointer block size + dsize: int; # data block size + depth: int; # unpacked from flags + flags: int; + size: big; # (XXX should be unsigned) + score: Venti->Score; + + new: fn(psize, dsize, flags: int, size: big, score: Venti->Score): ref Entry; + pack: fn(e: self ref Entry): array of byte; + unpack: fn(d: array of byte): ref Entry; + }; + + Direntry: adt { + version: int; + elem: string; + entry, gen: int; + mentry, mgen: int; + qid: big; + uid, gid, mid: string; + mtime, mcount, ctime, atime, mode, emode: int; + + new: fn(): ref Direntry; + mk: fn(d: Sys->Dir): ref Direntry; + mkdir: fn(de: self ref Direntry): ref Sys->Dir; + pack: fn(de: self ref Direntry): array of byte; + unpack: fn(d: array of byte): ref Direntry; + }; + + Metablock: adt { + size, free, maxindex, nindex: int; + + new: fn(): ref Metablock; + pack: fn(mb: self ref Metablock, d: array of byte); + unpack: fn(d: array of byte): ref Metablock; + }; + + Metaentry: adt { + offset, size: int; + + pack: fn(me: self ref Metaentry, d: array of byte); + unpack: fn(d: array of byte, i: int): ref Metaentry; + }; + + # single block + Page: adt { + d: array of byte; + o: int; + + new: fn(dsize: int): ref Page; + add: fn(p: self ref Page, s: Venti->Score); + full: fn(p: self ref Page): int; + data: fn(p: self ref Page): array of byte; + }; + + # hash tree file + File: adt { + p: array of ref Page; + dtype, dsize: int; + size: big; + s: ref Venti->Session; + + new: fn(s: ref Venti->Session, dtype, dsize: int): ref File; + write: fn(f: self ref File, d: array of byte): int; + finish: fn(f: self ref File): ref Entry; + }; + + # for writing venti directories + Sink: adt { + f: ref File; + d: array of byte; + nd, ne: int; + + new: fn(s: ref Venti->Session, dsize: int): ref Sink; + add: fn(m: self ref Sink, e: ref Entry): int; + finish: fn(m: self ref Sink): ref Entry; + }; + + Mentry: adt { + elem: string; + me: ref Metaentry; + + cmp: fn(a, b: ref Mentry): int; + }; + + # for writing directory entries (meta blocks, meta entries, direntries) + MSink: adt { + f: ref File; + de: array of byte; + nde: int; + l: list of ref Mentry; + + new: fn(s: ref Venti->Session, dsize: int): ref MSink; + add: fn(m: self ref MSink, de: ref Direntry): int; + finish: fn(m: self ref MSink): ref Entry; + }; + + # for reading pages from a hash tree referenced by an entry + Source: adt { + session: ref Venti->Session; + e: ref Entry; + + new: fn(s: ref Venti->Session, e: ref Entry): ref Source; + get: fn(s: self ref Source, i: big, d: array of byte): int; + }; + + # for reading from a hash tree while keeping offset + Vacfile: adt { + s: ref Source; + o: big; + + mk: fn(s: ref Source): ref Vacfile; + new: fn(session: ref Venti->Session, e: ref Entry): ref Vacfile; + read: fn(v: self ref Vacfile, d: array of byte, n: int): int; + seek: fn(v: self ref Vacfile, offset: big): big; + pread: fn(v: self ref Vacfile, d: array of byte, n: int, offset: big): int; + }; + + # for listing contents of a vac directory and walking to path elements + Vacdir: adt { + vf: ref Vacfile; + ms: ref Source; + p: big; + i: int; + + mk: fn(vf: ref Vacfile, ms: ref Source): ref Vacdir; + new: fn(session: ref Venti->Session, e, me: ref Entry): ref Vacdir; + walk: fn(v: self ref Vacdir, elem: string): ref Direntry; + open: fn(v: self ref Vacdir, de: ref Direntry): (ref Entry, ref Entry); + readdir: fn(v: self ref Vacdir): (int, ref Direntry); + rewind: fn(v: self ref Vacdir); + }; + + vdroot: fn(session: ref Venti->Session, score: Venti->Score): (ref Vacdir, ref Direntry, string); +}; |
