summaryrefslogtreecommitdiff
path: root/appl/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd')
-rw-r--r--appl/cmd/mkfile6
-rw-r--r--appl/cmd/vacfs.b446
-rw-r--r--appl/cmd/vacget.b199
-rw-r--r--appl/cmd/vacput.b258
4 files changed, 909 insertions, 0 deletions
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);
+}