ref: c116550e6a41572796e4db65e4f6acbcb3d9d6f8
dir: /appl/cmd/vacput.b/
implement Vacput;
include "sys.m";
sys: Sys;
sprint: import sys;
include "draw.m";
include "arg.m";
include "daytime.m";
dt: Daytime;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "dial.m";
dial: Dial;
include "string.m";
str: String;
include "tables.m";
tables: Tables;
Strhash: import tables;
include "venti.m";
venti: Venti;
Root, Entry, Score, Session: import venti;
include "vac.m";
vac: Vac;
Direntry, File, Sink, MSink: import vac;
Vacput: module {
init: fn(nil: ref Draw->Context, args: list of string);
};
addr := "$venti";
dflag: int;
iflag: int;
vflag: int;
xflag: int;
blocksize := vac->Dsize;
uid: string;
gid: string;
pathgen: big;
bout: ref Iobuf;
session: ref Session;
name := "vac";
itab,
xtab: ref Strhash[string]; # include/exclude paths
init(nil: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
dt = load Daytime Daytime->PATH;
bufio = load Bufio Bufio->PATH;
arg := load Arg Arg->PATH;
dial = load Dial Dial->PATH;
str = load String String->PATH;
tables = load Tables Tables->PATH;
venti = load Venti Venti->PATH;
vac = load Vac Vac->PATH;
if(venti == nil || vac == nil)
fail("loading venti,vac");
venti->init();
vac->init();
arg->init(args);
arg->setusage(arg->progname()+" [-dv] [-i | -x] [-a addr] [-b blocksize] [-n name] [-u uid] [-g gid] path ...");
while((c := arg->opt()) != 0)
case c {
'a' => addr = arg->earg();
'b' => blocksize = int arg->earg();
'n' => name = arg->earg();
'd' => vac->dflag = dflag++;
'i' => iflag++;
'v' => vflag++;
'x' => xflag++;
'g' => gid = arg->earg();
'u' => uid = arg->earg();
* => arg->usage();
}
args = arg->argv();
if(len args == 0)
arg->usage();
if(iflag && xflag) {
warn("cannot have both -i and -x");
arg->usage();
}
if(vflag)
bout = bufio->fopen(sys->fildes(1), bufio->OWRITE);
if(iflag || xflag) {
t := readpaths();
if(iflag)
itab = t;
else
xtab = t;
}
addr = dial->netmkaddr(addr, "net", "venti");
cc := dial->dial(addr, nil);
if(cc == nil)
fail(sprint("dialing %s: %r", addr));
say("have connection");
fd := cc.dfd;
session = Session.new(fd);
if(session == nil)
fail(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.qid = pathgen++;
if(uid != nil)
topde.uid = uid;
if(gid != nil)
topde.gid = gid;
topde.ctime = dt->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");
if(vflag && bout.flush() == bufio->ERROR)
fail(sprint("write stdout: %r"));
e0 := s.finish();
if(e0 == nil)
fail(sprint("writing top entry: %r"));
e1 := ms.finish();
if(e1 == nil)
fail(sprint("writing top meta entry: %r"));
topde.qidspace = 1;
topde.qidoff = big 0;
topde.qidmax = pathgen;
s2 := MSink.new(session, blocksize);
if(s2.add(topde) < 0)
fail(sprint("adding direntry for top entries: %r"));
e2 := s2.finish();
say("top meta entry written, "+e2.score.text());
td := array[venti->Entrysize*3] of byte;
td[0*venti->Entrysize:] = e0.pack();
td[1*venti->Entrysize:] = e1.pack();
td[2*venti->Entrysize:] = e2.pack();
(tok, tscore) := session.write(venti->Dirtype, td);
if(tok < 0)
fail(sprint("writing top-level entries: %r"));
root := ref Root(venti->Rootversion, name, "vac", tscore, blocksize, nil);
rd := root.pack();
if(rd == nil)
fail(sprint("root pack: %r"));
(rok, rscore) := session.write(venti->Roottype, rd);
if(rok < 0)
fail(sprint("writing root score: %r"));
sys->print("vac:%s\n", rscore.text());
if(session.sync() < 0)
fail(sprint("syncing server: %r"));
}
readpaths(): ref Strhash[string]
{
t := Strhash[string].new(199, nil);
b := bufio->fopen(sys->fildes(0), bufio->OREAD);
if(b == nil)
fail(sprint("fopen: %r"));
for(;;) {
s := b.gets('\n');
if(s == nil)
break;
if(s[len s-1] == '\n')
s = s[:len s-1];
t.add(s, s);
}
return t;
}
usepath(p: string): int
{
if(itab != nil)
return itab.find(p) != nil;
if(xtab != nil)
return xtab.find(p) == nil;
return 1;
}
writepath(path: string, s: ref Sink, ms: ref MSink)
{
if(!usepath(path))
return;
if(vflag && bout.puts(path+"\n") == bufio->ERROR)
fail(sprint("write stdout: %r"));
fd := sys->open(path, sys->OREAD);
if(fd == nil)
fail(sprint("opening %s: %r", path));
(ok, dir) := sys->fstat(fd);
if(ok < 0)
fail(sprint("fstat %s: %r", path));
if(dir.mode&sys->DMAUTH)
return warn(path+": is auth file, skipping");
if(dir.mode&sys->DMTMP)
return warn(path+": is temporary file, skipping");
e, me: ref Entry;
de: ref Direntry;
qid := pathgen++;
if(dir.mode & sys->DMDIR) {
ns := Sink.new(session, blocksize);
nms := MSink.new(session, blocksize);
for(;;) {
(n, dirs) := sys->dirread(fd);
if(n == 0)
break;
if(n < 0)
fail(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)
fail(sprint("error flushing dirsink for %s: %r", path));
me = nms.finish();
if(me == nil)
fail(sprint("error flushing metasink for %s: %r", path));
} else {
e = writefile(path, fd);
if(e == nil)
fail(sprint("error flushing filesink for %s: %r", path));
}
case dir.name {
"/" => dir.name = "root";
"." => dir.name = "dot";
}
de = Direntry.mk(dir);
de.qid = qid;
if(uid != nil)
de.uid = uid;
if(gid != nil)
de.gid = gid;
i := s.add(e);
if(i < 0)
fail(sprint("adding entry to sink: %r"));
mi := 0;
if(me != nil)
mi = s.add(me);
if(mi < 0)
fail(sprint("adding mentry to sink: %r"));
de.entry = i;
de.mentry = mi;
i = ms.add(de);
if(i < 0)
fail(sprint("adding direntry to msink: %r"));
}
writefile(path: string, fd: ref Sys->FD): ref Entry
{
bio := bufio->fopen(fd, bufio->OREAD);
if(bio == nil)
fail(sprint("bufio opening %s: %r", path));
f := File.new(session, venti->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)
fail(sprint("reading %s: %r", path));
n += have;
}
if(f.write(buf[:n]) < 0)
fail(sprint("writing %s: %r", path));
if(n != len buf)
break;
}
bio.close();
return f.finish();
}
user(): string
{
fd := sys->open("/dev/user", Sys->OREAD);
if(fd != nil)
if((n := sys->read(fd, d := array[128] of byte, len d)) > 0)
return string d[:n];
return "nobody";
}
fd2: ref Sys->FD;
warn(s: string)
{
if(fd2 == nil)
fd2 = sys->fildes(2);
sys->fprint(fd2, "%s\n", s);
}
say(s: string)
{
if(dflag)
warn(s);
}
fail(s: string)
{
warn(s);
raise "fail:"+s;
}