|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <venti.h> | 
|  | #include <libsec.h> | 
|  | #include <thread.h> | 
|  |  | 
|  | enum | 
|  | { | 
|  | // XXX What to do here? | 
|  | VtMaxLumpSize = 65535, | 
|  | }; | 
|  |  | 
|  | int chatty; | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: readfile [-v] [-h host] score\n"); | 
|  | threadexitsall("usage"); | 
|  | } | 
|  |  | 
|  | void | 
|  | threadmain(int argc, char *argv[]) | 
|  | { | 
|  | int n; | 
|  | uchar score[VtScoreSize]; | 
|  | uchar *buf; | 
|  | char *host, *type; | 
|  | vlong off; | 
|  | VtEntry e; | 
|  | VtRoot root; | 
|  | VtCache *c; | 
|  | VtConn *z; | 
|  | VtFile *f; | 
|  |  | 
|  | quotefmtinstall(); | 
|  | fmtinstall('F', vtfcallfmt); | 
|  | fmtinstall('V', vtscorefmt); | 
|  |  | 
|  | host = nil; | 
|  | ARGBEGIN{ | 
|  | case 'V': | 
|  | chattyventi++; | 
|  | break; | 
|  | case 'h': | 
|  | host = EARGF(usage()); | 
|  | break; | 
|  | case 'v': | 
|  | chatty++; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | break; | 
|  | }ARGEND | 
|  |  | 
|  | if(argc != 1) | 
|  | usage(); | 
|  |  | 
|  | type = nil; | 
|  | if(vtparsescore(argv[0], &type, score) < 0) | 
|  | sysfatal("could not parse score '%s': %r", argv[0]); | 
|  | if(type == nil || strcmp(type, "file") != 0) | 
|  | sysfatal("bad score - not file:..."); | 
|  |  | 
|  | buf = vtmallocz(VtMaxLumpSize); | 
|  |  | 
|  | z = vtdial(host); | 
|  | if(z == nil) | 
|  | sysfatal("could not connect to server: %r"); | 
|  |  | 
|  | if(vtconnect(z) < 0) | 
|  | sysfatal("vtconnect: %r"); | 
|  |  | 
|  | // root block ... | 
|  | n = vtread(z, score, VtRootType, buf, VtMaxLumpSize); | 
|  | if(n < 0) | 
|  | sysfatal("could not read root %V: %r", score); | 
|  | if(n != VtRootSize) | 
|  | sysfatal("root block %V is wrong size %d != %d", score, n, VtRootSize); | 
|  | if(vtrootunpack(&root, buf) < 0) | 
|  | sysfatal("unpacking root block %V: %r", score); | 
|  | if(strcmp(root.type, "file") != 0) | 
|  | sysfatal("bad root type %q (not 'file')", root.type); | 
|  | if(chatty) | 
|  | fprint(2, "%V: %q %q %V %d %V\n", | 
|  | score, root.name, root.type, | 
|  | root.score, root.blocksize, root.prev); | 
|  |  | 
|  | // ... points at entry block | 
|  | n = vtread(z, root.score, VtDirType, buf, VtMaxLumpSize); | 
|  | if(n < 0) | 
|  | sysfatal("could not read entry %V: %r", root.score); | 
|  | if(n != VtEntrySize) | 
|  | sysfatal("dir block %V is wrong size %d != %d", root.score, n, VtEntrySize); | 
|  | if(vtentryunpack(&e, buf, 0) < 0) | 
|  | sysfatal("unpacking dir block %V: %r", root.score); | 
|  | if((e.type&VtTypeBaseMask) != VtDataType) | 
|  | sysfatal("not a single file"); | 
|  |  | 
|  | // open and read file | 
|  | c = vtcachealloc(z, root.blocksize*32); | 
|  | if(c == nil) | 
|  | sysfatal("vtcachealloc: %r"); | 
|  | f = vtfileopenroot(c, &e); | 
|  | if(f == nil) | 
|  | sysfatal("vtfileopenroot: %r"); | 
|  | off = 0; | 
|  | vtfilelock(f, VtOREAD); | 
|  | while((n = vtfileread(f, buf, VtMaxLumpSize, off)) > 0){ | 
|  | write(1, buf, n); | 
|  | off += n; | 
|  | } | 
|  | threadexitsall(0); | 
|  | } |