mirror of
https://github.com/9fans/plan9port.git
synced 2025-01-15 11:20:03 +00:00
1213 lines
22 KiB
C
1213 lines
22 KiB
C
#include "stdinc.h"
|
|
#include "vac.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
|
|
typedef struct Sink Sink;
|
|
typedef struct MetaSink MetaSink;
|
|
typedef struct DirSink DirSink;
|
|
|
|
struct Sink {
|
|
VtSession *z;
|
|
VtEntry dir;
|
|
uchar *buf;
|
|
uchar *pbuf[VtPointerDepth+1];
|
|
};
|
|
|
|
struct DirSink {
|
|
Sink *sink;
|
|
MetaSink *msink;
|
|
ulong nentry;
|
|
uchar *buf;
|
|
uchar *p; /* current pointer */
|
|
uchar *ep; /* end pointer */
|
|
};
|
|
|
|
struct MetaSink {
|
|
Sink *sink;
|
|
uchar *buf;
|
|
int maxindex;
|
|
int nindex;
|
|
uchar *rp; /* start of current record */
|
|
uchar *p; /* current pointer */
|
|
uchar *ep; /* end pointer */
|
|
};
|
|
|
|
static void usage(void);
|
|
static int strpCmp(void*, void*);
|
|
static void warn(char *fmt, ...);
|
|
static void cleanup(void);
|
|
static u64int unittoull(char *s);
|
|
static int vac(VtSession *z, char *argv[]);
|
|
static void vacFile(DirSink *dsink, char *lname, char *sname, VacFile*);
|
|
static void vacStdin(DirSink *dsink, char *name, VacFile *vf);
|
|
static void vacData(DirSink *dsink, int fd, char *lname, VacFile*, Dir*);
|
|
static void vacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile*);
|
|
static int vacMerge(DirSink *dsink, char *lname, char *sname);
|
|
|
|
Sink *sinkAlloc(VtSession *z, int psize, int dsize);
|
|
void sinkWrite(Sink *k, uchar *data, int n);
|
|
void sinkWriteScore(Sink *k, uchar *score, int n);
|
|
void sinkClose(Sink *k);
|
|
void sinkFree(Sink *k);
|
|
|
|
DirSink *dirSinkAlloc(VtSession *z, int psize, int dsize);
|
|
void dirSinkWrite(DirSink *k, VtEntry*);
|
|
void dirSinkWriteSink(DirSink *k, Sink*);
|
|
int dirSinkWriteFile(DirSink *k, VacFile *vf);
|
|
void dirSinkClose(DirSink *k);
|
|
void dirSinkFree(DirSink *k);
|
|
|
|
MetaSink *metaSinkAlloc(VtSession *z, int psize, int dsize);
|
|
void metaSinkPutc(MetaSink *k, int c);
|
|
void metaSinkPutString(MetaSink *k, char *s);
|
|
void metaSinkPutUint32(MetaSink *k, ulong x);
|
|
void metaSinkPutUint64(MetaSink *k, uvlong x);
|
|
void metaSinkWrite(MetaSink *k, uchar *data, int n);
|
|
void metaSinkWriteDir(MetaSink *ms, VacDir *vd);
|
|
void metaSinkEOR(MetaSink *k);
|
|
void metaSinkClose(MetaSink *k);
|
|
void metaSinkFree(MetaSink *k);
|
|
void plan9ToVacDir(VacDir*, Dir*, ulong entry, uvlong qid);
|
|
|
|
enum {
|
|
Version = 8,
|
|
BlockSize = 8*1024,
|
|
MaxExclude = 1000,
|
|
};
|
|
|
|
struct {
|
|
ulong file;
|
|
ulong sfile;
|
|
ulong data;
|
|
ulong sdata;
|
|
ulong skip;
|
|
ulong meta;
|
|
} stats;
|
|
|
|
int bsize = BlockSize;
|
|
int maxbsize;
|
|
char *oname, *dfile;
|
|
int verbose;
|
|
uvlong fileid = 1;
|
|
int qdiff;
|
|
char *exclude[MaxExclude];
|
|
int nexclude;
|
|
int nowrite;
|
|
int merge;
|
|
char *isi;
|
|
|
|
void
|
|
main(int argc, char *argv[])
|
|
{
|
|
VtSession *z;
|
|
char *p;
|
|
char *host = nil;
|
|
int statsFlag = 0;
|
|
|
|
atexit(cleanup);
|
|
|
|
ARGBEGIN{
|
|
default:
|
|
usage();
|
|
case 'b':
|
|
p = ARGF();
|
|
if(p == 0)
|
|
usage();
|
|
bsize = unittoull(p);
|
|
if(bsize == ~0)
|
|
usage();
|
|
break;
|
|
case 'd':
|
|
dfile = ARGF();
|
|
if(dfile == nil)
|
|
usage();
|
|
break;
|
|
case 'e':
|
|
if(nexclude >= MaxExclude)
|
|
sysfatal("too many exclusions\n");
|
|
exclude[nexclude] = ARGF();
|
|
if(exclude[nexclude] == nil)
|
|
usage();
|
|
nexclude++;
|
|
break;
|
|
case 'f':
|
|
oname = ARGF();
|
|
if(oname == 0)
|
|
usage();
|
|
break;
|
|
case 'h':
|
|
host = ARGF();
|
|
if(host == nil)
|
|
usage();
|
|
break;
|
|
case 'i':
|
|
isi = ARGF();
|
|
if(isi == nil)
|
|
usage();
|
|
break;
|
|
case 'n':
|
|
nowrite++;
|
|
break;
|
|
case 'm':
|
|
merge++;
|
|
break;
|
|
case 'q':
|
|
qdiff++;
|
|
break;
|
|
case 's':
|
|
statsFlag++;
|
|
break;
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
}ARGEND;
|
|
|
|
if(bsize < 512)
|
|
bsize = 512;
|
|
if(bsize > VtMaxLumpSize)
|
|
bsize = VtMaxLumpSize;
|
|
maxbsize = bsize;
|
|
|
|
vtAttach();
|
|
|
|
fmtinstall('V', vtScoreFmt);
|
|
fmtinstall('R', vtErrFmt);
|
|
|
|
z = vtDial(host, 0);
|
|
if(z == nil)
|
|
vtFatal("could not connect to server: %R");
|
|
|
|
if(!vtConnect(z, 0))
|
|
vtFatal("vtConnect: %R");
|
|
|
|
qsort(exclude, nexclude, sizeof(char*), strpCmp);
|
|
|
|
vac(z, argv);
|
|
if(!vtSync(z))
|
|
fprint(2, "warning: could not ask server to flush pending writes: %R\n");
|
|
|
|
if(statsFlag)
|
|
fprint(2, "files %ld:%ld data %ld:%ld:%ld meta %ld\n", stats.file, stats.sfile,
|
|
stats.data, stats.skip, stats.sdata, stats.meta);
|
|
//packetStats();
|
|
vtClose(z);
|
|
vtDetach();
|
|
|
|
exits(0);
|
|
}
|
|
|
|
void
|
|
static usage(void)
|
|
{
|
|
fprint(2, "usage: %s [-amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-f vacfile] file ... \n", argv0);
|
|
exits("usage");
|
|
}
|
|
|
|
static
|
|
int strpCmp(void *p0, void *p1)
|
|
{
|
|
return strcmp(*(char**)p0, *(char**)p1);
|
|
}
|
|
|
|
|
|
int
|
|
readBlock(int fd, uchar *buf, int n)
|
|
{
|
|
int m, t = 0;
|
|
|
|
while(t < n){
|
|
m = read(fd, buf+t, n-t);
|
|
if(m < 0)
|
|
return -1;
|
|
if(m == 0)
|
|
break;
|
|
t += m;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
int
|
|
vacWrite(VtSession *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
|
|
{
|
|
assert(n > 0);
|
|
if(nowrite) {
|
|
vtSha1(score, buf, n);
|
|
return 1;
|
|
}
|
|
if(!vtWrite(z, score, type, buf, n))
|
|
return 0;
|
|
if(!vtSha1Check(score, buf, n)) {
|
|
uchar score2[VtScoreSize];
|
|
|
|
vtSha1(score2, buf, n);
|
|
fprint(2, "vtSha1Check: n = %d %V %V\n", n, score, score2);
|
|
vtSetError("vtSha1Check failed");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
vac(VtSession *z, char *argv[])
|
|
{
|
|
DirSink *dsink, *ds;
|
|
MetaSink *ms;
|
|
VtRoot root;
|
|
uchar score[VtScoreSize], buf[VtRootSize];
|
|
char cwd[2048];
|
|
int cd, i;
|
|
char *cp2, *cp;
|
|
VacFS *fs;
|
|
VacFile *vff;
|
|
int fd;
|
|
Dir *dir;
|
|
VacDir vd;
|
|
|
|
if(getwd(cwd, sizeof(cwd)) == 0)
|
|
sysfatal("can't find current directory: %r\n");
|
|
|
|
dsink = dirSinkAlloc(z, bsize, bsize);
|
|
|
|
fs = nil;
|
|
if(dfile != nil) {
|
|
fs = vfsOpen(z, dfile, 1, 10000);
|
|
if(fs == nil)
|
|
fprint(2, "could not open diff: %s: %s\n", dfile, vtGetError());
|
|
}
|
|
|
|
|
|
if(oname != nil) {
|
|
fd = create(oname, OWRITE, 0666);
|
|
if(fd < 0)
|
|
sysfatal("could not create file: %s: %r", oname);
|
|
} else
|
|
fd = 1;
|
|
|
|
dir = dirfstat(fd);
|
|
if(dir == nil)
|
|
sysfatal("dirfstat failed: %r");
|
|
|
|
for(; *argv; argv++) {
|
|
cp2 = *argv;
|
|
cd = 0;
|
|
for (cp = *argv; *cp; cp++)
|
|
if (*cp == '/')
|
|
cp2 = cp;
|
|
if (cp2 != *argv) {
|
|
*cp2 = '\0';
|
|
chdir(*argv);
|
|
*cp2 = '/';
|
|
cp2++;
|
|
cd = 1;
|
|
}
|
|
vff = nil;
|
|
if(fs)
|
|
vff = vfOpen(fs, cp2);
|
|
vacFile(dsink, argv[0], cp2, vff);
|
|
if(vff)
|
|
vfDecRef(vff);
|
|
if(cd && chdir(cwd) < 0)
|
|
sysfatal("can't cd back to %s: %r\n", cwd);
|
|
}
|
|
|
|
if(isi) {
|
|
vff = nil;
|
|
if(fs)
|
|
vff = vfOpen(fs, isi);
|
|
vacStdin(dsink, isi, vff);
|
|
if(vff)
|
|
vfDecRef(vff);
|
|
}
|
|
|
|
dirSinkClose(dsink);
|
|
|
|
/* build meta information for the root */
|
|
ms = metaSinkAlloc(z, bsize, bsize);
|
|
/* fake into a directory */
|
|
dir->mode |= (dir->mode&0444)>>2;
|
|
dir->qid.type |= QTDIR;
|
|
dir->mode |= DMDIR;
|
|
plan9ToVacDir(&vd, dir, 0, fileid++);
|
|
if(strcmp(vd.elem, "/") == 0){
|
|
vtMemFree(vd.elem);
|
|
vd.elem = vtStrDup("root");
|
|
}
|
|
metaSinkWriteDir(ms, &vd);
|
|
vdCleanup(&vd);
|
|
metaSinkClose(ms);
|
|
|
|
ds = dirSinkAlloc(z, bsize, bsize);
|
|
dirSinkWriteSink(ds, dsink->sink);
|
|
dirSinkWriteSink(ds, dsink->msink->sink);
|
|
dirSinkWriteSink(ds, ms->sink);
|
|
dirSinkClose(ds);
|
|
|
|
memset(&root, 0, sizeof(root));
|
|
root.version = VtRootVersion;
|
|
strncpy(root.name, dir->name, sizeof(root.name));
|
|
root.name[sizeof(root.name)-1] = 0;
|
|
free(dir);
|
|
sprint(root.type, "vac");
|
|
memmove(root.score, ds->sink->dir.score, VtScoreSize);
|
|
root.blockSize = maxbsize;
|
|
if(fs != nil)
|
|
vfsGetScore(fs, root.prev);
|
|
|
|
metaSinkFree(ms);
|
|
dirSinkFree(ds);
|
|
dirSinkFree(dsink);
|
|
if(fs != nil)
|
|
vfsClose(fs);
|
|
|
|
vtRootPack(&root, buf);
|
|
if(!vacWrite(z, score, VtRootType, buf, VtRootSize))
|
|
vtFatal("vacWrite failed: %s", vtGetError());
|
|
|
|
fprint(fd, "vac:");
|
|
for(i=0; i<VtScoreSize; i++)
|
|
fprint(fd, "%.2x", score[i]);
|
|
fprint(fd, "\n");
|
|
|
|
/* avoid remove at cleanup */
|
|
oname = nil;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
isExcluded(char *name)
|
|
{
|
|
int bot, top, i, x;
|
|
|
|
bot = 0;
|
|
top = nexclude;
|
|
while(bot < top) {
|
|
i = (bot+top)>>1;
|
|
x = strcmp(exclude[i], name);
|
|
if(x == 0)
|
|
return 1;
|
|
if(x < 0)
|
|
bot = i + 1;
|
|
else /* x > 0 */
|
|
top = i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
vacFile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
|
|
{
|
|
int fd;
|
|
Dir *dir;
|
|
VacDir vd;
|
|
ulong entry;
|
|
|
|
if(isExcluded(lname)) {
|
|
warn("excluding: %s", lname);
|
|
return;
|
|
}
|
|
|
|
if(merge && vacMerge(dsink, lname, sname))
|
|
return;
|
|
|
|
fd = open(sname, OREAD);
|
|
if(fd < 0) {
|
|
warn("could not open file: %s: %s", lname, vtOSError());
|
|
return;
|
|
}
|
|
|
|
if(verbose)
|
|
fprint(2, "%s\n", lname);
|
|
|
|
dir = dirfstat(fd);
|
|
if(dir == nil) {
|
|
warn("can't stat %s: %r", lname);
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
entry = dsink->nentry;
|
|
|
|
if(dir->mode & DMDIR)
|
|
vacDir(dsink, fd, lname, sname, vf);
|
|
else
|
|
vacData(dsink, fd, lname, vf, dir);
|
|
|
|
plan9ToVacDir(&vd, dir, entry, fileid++);
|
|
metaSinkWriteDir(dsink->msink, &vd);
|
|
vdCleanup(&vd);
|
|
|
|
free(dir);
|
|
close(fd);
|
|
}
|
|
|
|
static void
|
|
vacStdin(DirSink *dsink, char *name, VacFile *vf)
|
|
{
|
|
Dir *dir;
|
|
VacDir vd;
|
|
ulong entry;
|
|
|
|
if(verbose)
|
|
fprint(2, "%s\n", "<stdio>");
|
|
|
|
dir = dirfstat(0);
|
|
if(dir == nil) {
|
|
warn("can't stat <stdio>: %r");
|
|
return;
|
|
}
|
|
|
|
entry = dsink->nentry;
|
|
|
|
vacData(dsink, 0, "<stdin>", vf, dir);
|
|
|
|
plan9ToVacDir(&vd, dir, entry, fileid++);
|
|
vd.elem = vtStrDup(name);
|
|
metaSinkWriteDir(dsink->msink, &vd);
|
|
vdCleanup(&vd);
|
|
|
|
free(dir);
|
|
}
|
|
|
|
static ulong
|
|
vacDataSkip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
|
|
{
|
|
int n;
|
|
ulong i;
|
|
uchar score[VtScoreSize];
|
|
|
|
/* skip blocks for append only files */
|
|
if(seek(fd, (blocks-1)*bsize, 0) != (blocks-1)*bsize) {
|
|
warn("error seeking: %s", lname);
|
|
goto Err;
|
|
}
|
|
n = readBlock(fd, buf, bsize);
|
|
if(n < bsize) {
|
|
warn("error checking append only file: %s", lname);
|
|
goto Err;
|
|
}
|
|
if(!vfGetBlockScore(vf, blocks-1, score) || !vtSha1Check(score, buf, n)) {
|
|
warn("last block of append file did not match: %s", lname);
|
|
goto Err;
|
|
}
|
|
|
|
for(i=0; i<blocks; i++) {
|
|
if(!vfGetBlockScore(vf, i, score)) {
|
|
warn("could not get score: %s: %lud", lname, i);
|
|
seek(fd, i*bsize, 0);
|
|
return i;
|
|
}
|
|
stats.skip++;
|
|
sinkWriteScore(sink, score, bsize);
|
|
}
|
|
|
|
return i;
|
|
Err:
|
|
seek(fd, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
vacData(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
|
|
{
|
|
uchar *buf;
|
|
Sink *sink;
|
|
int n;
|
|
uchar score[VtScoreSize];
|
|
ulong block, same;
|
|
VacDir vd;
|
|
ulong vfblocks;
|
|
|
|
vfblocks = 0;
|
|
if(vf != nil && qdiff) {
|
|
vfGetDir(vf, &vd);
|
|
if(vd.mtime == dir->mtime)
|
|
if(vd.size == dir->length)
|
|
if(!vd.plan9 || /* vd.p9path == dir->qid.path && */ vd.p9version == dir->qid.vers)
|
|
if(dirSinkWriteFile(dsink, vf)) {
|
|
stats.sfile++;
|
|
vdCleanup(&vd);
|
|
return;
|
|
}
|
|
|
|
/* look for an append only file */
|
|
if((dir->mode&DMAPPEND) != 0)
|
|
if(vd.size < dir->length)
|
|
if(vd.plan9)
|
|
if(vd.p9path == dir->qid.path)
|
|
vfblocks = vd.size/bsize;
|
|
|
|
vdCleanup(&vd);
|
|
}
|
|
stats.file++;
|
|
|
|
buf = vtMemAlloc(bsize);
|
|
sink = sinkAlloc(dsink->sink->z, bsize, bsize);
|
|
block = 0;
|
|
same = stats.sdata+stats.skip;
|
|
|
|
if(vfblocks > 1)
|
|
block += vacDataSkip(sink, vf, fd, vfblocks, buf, lname);
|
|
|
|
if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
|
|
for(;;) {
|
|
n = readBlock(fd, buf, bsize);
|
|
if(0 && n < 0)
|
|
warn("file truncated due to read error: %s: %s", lname, vtOSError());
|
|
if(n <= 0)
|
|
break;
|
|
if(vf != nil && vfGetBlockScore(vf, block, score) && vtSha1Check(score, buf, n)) {
|
|
stats.sdata++;
|
|
sinkWriteScore(sink, score, n);
|
|
} else
|
|
sinkWrite(sink, buf, n);
|
|
block++;
|
|
}
|
|
same = stats.sdata+stats.skip - same;
|
|
|
|
if(same && (dir->mode&DMAPPEND) != 0)
|
|
if(0)fprint(2, "%s: total %lud same %lud:%lud diff %lud\n",
|
|
lname, block, same, vfblocks, block-same);
|
|
|
|
sinkClose(sink);
|
|
dirSinkWriteSink(dsink, sink);
|
|
sinkFree(sink);
|
|
free(buf);
|
|
}
|
|
|
|
|
|
static void
|
|
vacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
|
|
{
|
|
Dir *dirs;
|
|
char *ln, *sn;
|
|
int i, nd;
|
|
DirSink *ds;
|
|
VacFile *vvf;
|
|
char *name;
|
|
|
|
ds = dirSinkAlloc(dsink->sink->z, bsize, bsize);
|
|
while((nd = dirread(fd, &dirs)) > 0){
|
|
for(i = 0; i < nd; i++){
|
|
name = dirs[i].name;
|
|
/* check for bad file names */
|
|
if(name[0] == 0 || strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
|
|
continue;
|
|
ln = vtMemAlloc(strlen(lname) + strlen(name) + 2);
|
|
sn = vtMemAlloc(strlen(sname) + strlen(name) + 2);
|
|
sprint(ln, "%s/%s", lname, name);
|
|
sprint(sn, "%s/%s", sname, name);
|
|
if(vf != nil)
|
|
vvf = vfWalk(vf, name);
|
|
else
|
|
vvf = nil;
|
|
vacFile(ds, ln, sn, vvf);
|
|
if(vvf != nil)
|
|
vfDecRef(vvf);
|
|
vtMemFree(ln);
|
|
vtMemFree(sn);
|
|
}
|
|
free(dirs);
|
|
}
|
|
dirSinkClose(ds);
|
|
dirSinkWriteSink(dsink, ds->sink);
|
|
dirSinkWriteSink(dsink, ds->msink->sink);
|
|
dirSinkFree(ds);
|
|
}
|
|
|
|
static int
|
|
vacMergeFile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *max)
|
|
{
|
|
uchar buf[VtEntrySize];
|
|
VtEntry dd, md;
|
|
int e;
|
|
|
|
if(vfRead(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
|
|
warn("could not read venti dir entry: %s\n", dir->elem);
|
|
return 0;
|
|
}
|
|
vtEntryUnpack(&dd, buf, 0);
|
|
|
|
if(dir->mode & ModeDir) {
|
|
e = dir->mentry;
|
|
if(e == 0)
|
|
e = dir->entry + 1;
|
|
|
|
if(vfRead(vf, buf, VtEntrySize, e*VtEntrySize) != VtEntrySize) {
|
|
warn("could not read venti dir entry: %s\n", dir->elem);
|
|
return 0;
|
|
}
|
|
vtEntryUnpack(&md, buf, 0);
|
|
}
|
|
|
|
/* max might incorrect in some old dumps */
|
|
if(dir->qid >= *max) {
|
|
warn("qid out of range: %s", dir->elem);
|
|
*max = dir->qid;
|
|
}
|
|
|
|
dir->qid += offset;
|
|
dir->entry = dsink->nentry;
|
|
|
|
if(dir->qidSpace) {
|
|
dir->qidOffset += offset;
|
|
} else {
|
|
dir->qidSpace = 1;
|
|
dir->qidOffset = offset;
|
|
dir->qidMax = *max;
|
|
}
|
|
|
|
dirSinkWrite(dsink, &dd);
|
|
if(dir->mode & ModeDir)
|
|
dirSinkWrite(dsink, &md);
|
|
metaSinkWriteDir(dsink->msink, dir);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
vacMerge(DirSink *dsink, char *lname, char *sname)
|
|
{
|
|
char *p;
|
|
VacFS *fs;
|
|
VacFile *vf;
|
|
VacDirEnum *d;
|
|
VacDir dir;
|
|
uvlong max;
|
|
|
|
p = strrchr(sname, '.');
|
|
if(p == 0 || strcmp(p, ".vac"))
|
|
return 0;
|
|
|
|
d = nil;
|
|
fs = vfsOpen(dsink->sink->z, sname, 1, 100);
|
|
if(fs == nil)
|
|
return 0;
|
|
|
|
vf = vfOpen(fs, "/");
|
|
if(vf == nil)
|
|
goto Done;
|
|
max = vfGetId(vf);
|
|
d = vdeOpen(fs, "/");
|
|
if(d == nil)
|
|
goto Done;
|
|
|
|
if(verbose)
|
|
fprint(2, "merging: %s\n", lname);
|
|
|
|
if(maxbsize < vfsGetBlockSize(fs))
|
|
maxbsize = vfsGetBlockSize(fs);
|
|
|
|
for(;;) {
|
|
if(vdeRead(d, &dir, 1) < 1)
|
|
break;
|
|
vacMergeFile(dsink, vf, &dir, fileid, &max);
|
|
vdCleanup(&dir);
|
|
}
|
|
fileid += max;
|
|
|
|
Done:
|
|
if(d != nil)
|
|
vdeFree(d);
|
|
if(vf != nil)
|
|
vfDecRef(vf);
|
|
vfsClose(fs);
|
|
return 1;
|
|
}
|
|
|
|
Sink *
|
|
sinkAlloc(VtSession *z, int psize, int dsize)
|
|
{
|
|
Sink *k;
|
|
int i;
|
|
|
|
if(psize < 512 || psize > VtMaxLumpSize)
|
|
vtFatal("sinkAlloc: bad psize");
|
|
if(dsize < 512 || dsize > VtMaxLumpSize)
|
|
vtFatal("sinkAlloc: bad psize");
|
|
|
|
psize = VtScoreSize*(psize/VtScoreSize);
|
|
|
|
k = vtMemAllocZ(sizeof(Sink));
|
|
k->z = z;
|
|
k->dir.flags = VtEntryActive;
|
|
k->dir.psize = psize;
|
|
k->dir.dsize = dsize;
|
|
k->buf = vtMemAllocZ(VtPointerDepth*k->dir.psize + VtScoreSize);
|
|
for(i=0; i<=VtPointerDepth; i++)
|
|
k->pbuf[i] = k->buf + i*k->dir.psize;
|
|
return k;
|
|
}
|
|
|
|
void
|
|
sinkWriteScore(Sink *k, uchar score[VtScoreSize], int n)
|
|
{
|
|
int i;
|
|
uchar *p;
|
|
VtEntry *d;
|
|
|
|
memmove(k->pbuf[0], score, VtScoreSize);
|
|
|
|
d = &k->dir;
|
|
|
|
for(i=0; i<VtPointerDepth; i++) {
|
|
k->pbuf[i] += VtScoreSize;
|
|
if(k->pbuf[i] < k->buf + d->psize*(i+1))
|
|
break;
|
|
if(i == VtPointerDepth-1)
|
|
vtFatal("file too big");
|
|
p = k->buf+i*d->psize;
|
|
stats.meta++;
|
|
if(!vacWrite(k->z, k->pbuf[i+1], VtPointerType0+i, p, d->psize))
|
|
vtFatal("vacWrite failed: %s", vtGetError());
|
|
k->pbuf[i] = p;
|
|
}
|
|
|
|
/* round size up to multiple of dsize */
|
|
d->size = d->dsize * ((d->size + d->dsize-1)/d->dsize);
|
|
|
|
d->size += n;
|
|
}
|
|
|
|
void
|
|
sinkWrite(Sink *k, uchar *p, int n)
|
|
{
|
|
int type;
|
|
uchar score[VtScoreSize];
|
|
|
|
if(n > k->dir.dsize)
|
|
vtFatal("sinkWrite: size too big");
|
|
|
|
if(k->dir.flags & VtEntryDir) {
|
|
type = VtDirType;
|
|
stats.meta++;
|
|
} else {
|
|
type = VtDataType;
|
|
stats.data++;
|
|
}
|
|
if(!vacWrite(k->z, score, type, p, n))
|
|
vtFatal("vacWrite failed: %s", vtGetError());
|
|
|
|
sinkWriteScore(k, score, n);
|
|
}
|
|
|
|
static int
|
|
sizeToDepth(uvlong s, int psize, int dsize)
|
|
{
|
|
int np;
|
|
int d;
|
|
|
|
/* determine pointer depth */
|
|
np = psize/VtScoreSize;
|
|
s = (s + dsize - 1)/dsize;
|
|
for(d = 0; s > 1; d++)
|
|
s = (s + np - 1)/np;
|
|
return d;
|
|
}
|
|
|
|
void
|
|
sinkClose(Sink *k)
|
|
{
|
|
int i, n;
|
|
uchar *p;
|
|
VtEntry *kd;
|
|
|
|
kd = &k->dir;
|
|
|
|
/* empty */
|
|
if(kd->size == 0) {
|
|
memmove(kd->score, vtZeroScore, VtScoreSize);
|
|
return;
|
|
}
|
|
|
|
for(n=VtPointerDepth-1; n>0; n--)
|
|
if(k->pbuf[n] > k->buf + kd->psize*n)
|
|
break;
|
|
|
|
kd->depth = sizeToDepth(kd->size, kd->psize, kd->dsize);
|
|
|
|
/* skip full part of tree */
|
|
for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
|
|
;
|
|
|
|
/* is the tree completely full */
|
|
if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
|
|
memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
|
|
return;
|
|
}
|
|
n++;
|
|
|
|
/* clean up the edge */
|
|
for(; i<n; i++) {
|
|
p = k->buf+i*kd->psize;
|
|
stats.meta++;
|
|
if(!vacWrite(k->z, k->pbuf[i+1], VtPointerType0+i, p, k->pbuf[i]-p))
|
|
vtFatal("vacWrite failed: %s", vtGetError());
|
|
k->pbuf[i+1] += VtScoreSize;
|
|
}
|
|
memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
|
|
}
|
|
|
|
void
|
|
sinkFree(Sink *k)
|
|
{
|
|
vtMemFree(k->buf);
|
|
vtMemFree(k);
|
|
}
|
|
|
|
DirSink *
|
|
dirSinkAlloc(VtSession *z, int psize, int dsize)
|
|
{
|
|
DirSink *k;
|
|
int ds;
|
|
|
|
ds = VtEntrySize*(dsize/VtEntrySize);
|
|
|
|
k = vtMemAllocZ(sizeof(DirSink));
|
|
k->sink = sinkAlloc(z, psize, ds);
|
|
k->sink->dir.flags |= VtEntryDir;
|
|
k->msink = metaSinkAlloc(z, psize, dsize);
|
|
k->buf = vtMemAlloc(ds);
|
|
k->p = k->buf;
|
|
k->ep = k->buf + ds;
|
|
return k;
|
|
}
|
|
|
|
void
|
|
dirSinkWrite(DirSink *k, VtEntry *dir)
|
|
{
|
|
if(k->p + VtEntrySize > k->ep) {
|
|
sinkWrite(k->sink, k->buf, k->p - k->buf);
|
|
k->p = k->buf;
|
|
}
|
|
vtEntryPack(dir, k->p, 0);
|
|
k->nentry++;
|
|
k->p += VtEntrySize;
|
|
}
|
|
|
|
void
|
|
dirSinkWriteSink(DirSink *k, Sink *sink)
|
|
{
|
|
dirSinkWrite(k, &sink->dir);
|
|
}
|
|
|
|
int
|
|
dirSinkWriteFile(DirSink *k, VacFile *vf)
|
|
{
|
|
VtEntry dir;
|
|
|
|
if(!vfGetVtEntry(vf, &dir))
|
|
return 0;
|
|
dirSinkWrite(k, &dir);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
dirSinkClose(DirSink *k)
|
|
{
|
|
metaSinkClose(k->msink);
|
|
if(k->p != k->buf)
|
|
sinkWrite(k->sink, k->buf, k->p - k->buf);
|
|
sinkClose(k->sink);
|
|
}
|
|
|
|
void
|
|
dirSinkFree(DirSink *k)
|
|
{
|
|
sinkFree(k->sink);
|
|
metaSinkFree(k->msink);
|
|
vtMemFree(k->buf);
|
|
vtMemFree(k);
|
|
}
|
|
|
|
MetaSink *
|
|
metaSinkAlloc(VtSession *z, int psize, int dsize)
|
|
{
|
|
MetaSink *k;
|
|
|
|
k = vtMemAllocZ(sizeof(MetaSink));
|
|
k->sink = sinkAlloc(z, psize, dsize);
|
|
k->buf = vtMemAlloc(dsize);
|
|
k->maxindex = dsize/100; /* 100 byte entries seems reasonable */
|
|
if(k->maxindex < 1)
|
|
k->maxindex = 1;
|
|
k->rp = k->p = k->buf + MetaHeaderSize + k->maxindex*MetaIndexSize;
|
|
k->ep = k->buf + dsize;
|
|
return k;
|
|
}
|
|
|
|
/* hack to get base to compare routine - not reentrant */
|
|
uchar *blockBase;
|
|
|
|
int
|
|
dirCmp(void *p0, void *p1)
|
|
{
|
|
uchar *q0, *q1;
|
|
int n0, n1, r;
|
|
|
|
/* name is first element of entry */
|
|
q0 = p0;
|
|
q0 = blockBase + (q0[0]<<8) + q0[1];
|
|
n0 = (q0[6]<<8) + q0[7];
|
|
q0 += 8;
|
|
|
|
q1 = p1;
|
|
q1 = blockBase + (q1[0]<<8) + q1[1];
|
|
n1 = (q1[6]<<8) + q1[7];
|
|
q1 += 8;
|
|
|
|
if(n0 == n1)
|
|
return memcmp(q0, q1, n0);
|
|
else if (n0 < n1) {
|
|
r = memcmp(q0, q1, n0);
|
|
return (r==0)?1:r;
|
|
} else {
|
|
r = memcmp(q0, q1, n1);
|
|
return (r==0)?-1:r;
|
|
}
|
|
}
|
|
|
|
void
|
|
metaSinkFlush(MetaSink *k)
|
|
{
|
|
uchar *p;
|
|
int n;
|
|
MetaBlock mb;
|
|
|
|
if(k->nindex == 0)
|
|
return;
|
|
assert(k->nindex <= k->maxindex);
|
|
|
|
p = k->buf;
|
|
n = k->rp - p;
|
|
|
|
mb.size = n;
|
|
mb.free = 0;
|
|
mb.nindex = k->nindex;
|
|
mb.maxindex = k->maxindex;
|
|
mb.buf = p;
|
|
mbPack(&mb);
|
|
|
|
p += MetaHeaderSize;
|
|
|
|
/* XXX this is not reentrant! */
|
|
blockBase = k->buf;
|
|
qsort(p, k->nindex, MetaIndexSize, dirCmp);
|
|
p += k->nindex*MetaIndexSize;
|
|
|
|
memset(p, 0, (k->maxindex-k->nindex)*MetaIndexSize);
|
|
p += (k->maxindex-k->nindex)*MetaIndexSize;
|
|
|
|
sinkWrite(k->sink, k->buf, n);
|
|
|
|
/* move down partial entry */
|
|
n = k->p - k->rp;
|
|
memmove(p, k->rp, n);
|
|
k->rp = p;
|
|
k->p = p + n;
|
|
k->nindex = 0;
|
|
}
|
|
|
|
void
|
|
metaSinkPutc(MetaSink *k, int c)
|
|
{
|
|
if(k->p+1 > k->ep)
|
|
metaSinkFlush(k);
|
|
if(k->p+1 > k->ep)
|
|
vtFatal("directory entry too large");
|
|
k->p[0] = c;
|
|
k->p++;
|
|
}
|
|
|
|
void
|
|
metaSinkPutString(MetaSink *k, char *s)
|
|
{
|
|
int n = strlen(s);
|
|
metaSinkPutc(k, n>>8);
|
|
metaSinkPutc(k, n);
|
|
metaSinkWrite(k, (uchar*)s, n);
|
|
}
|
|
|
|
void
|
|
metaSinkPutUint32(MetaSink *k, ulong x)
|
|
{
|
|
metaSinkPutc(k, x>>24);
|
|
metaSinkPutc(k, x>>16);
|
|
metaSinkPutc(k, x>>8);
|
|
metaSinkPutc(k, x);
|
|
}
|
|
|
|
void
|
|
metaSinkPutUint64(MetaSink *k, uvlong x)
|
|
{
|
|
metaSinkPutUint32(k, x>>32);
|
|
metaSinkPutUint32(k, x);
|
|
}
|
|
|
|
void
|
|
metaSinkWrite(MetaSink *k, uchar *data, int n)
|
|
{
|
|
if(k->p + n > k->ep)
|
|
metaSinkFlush(k);
|
|
if(k->p + n > k->ep)
|
|
vtFatal("directory entry too large");
|
|
|
|
memmove(k->p, data, n);
|
|
k->p += n;
|
|
}
|
|
|
|
void
|
|
metaSinkWriteDir(MetaSink *ms, VacDir *dir)
|
|
{
|
|
metaSinkPutUint32(ms, DirMagic);
|
|
metaSinkPutc(ms, Version>>8);
|
|
metaSinkPutc(ms, Version);
|
|
metaSinkPutString(ms, dir->elem);
|
|
metaSinkPutUint32(ms, dir->entry);
|
|
metaSinkPutUint64(ms, dir->qid);
|
|
metaSinkPutString(ms, dir->uid);
|
|
metaSinkPutString(ms, dir->gid);
|
|
metaSinkPutString(ms, dir->mid);
|
|
metaSinkPutUint32(ms, dir->mtime);
|
|
metaSinkPutUint32(ms, dir->mcount);
|
|
metaSinkPutUint32(ms, dir->ctime);
|
|
metaSinkPutUint32(ms, dir->atime);
|
|
metaSinkPutUint32(ms, dir->mode);
|
|
|
|
if(dir->plan9) {
|
|
metaSinkPutc(ms, DirPlan9Entry); /* plan9 extra info */
|
|
metaSinkPutc(ms, 0); /* plan9 extra size */
|
|
metaSinkPutc(ms, 12); /* plan9 extra size */
|
|
metaSinkPutUint64(ms, dir->p9path);
|
|
metaSinkPutUint32(ms, dir->p9version);
|
|
}
|
|
|
|
if(dir->qidSpace != 0) {
|
|
metaSinkPutc(ms, DirQidSpaceEntry);
|
|
metaSinkPutc(ms, 0);
|
|
metaSinkPutc(ms, 16);
|
|
metaSinkPutUint64(ms, dir->qidOffset);
|
|
metaSinkPutUint64(ms, dir->qidMax);
|
|
}
|
|
|
|
if(dir->gen != 0) {
|
|
metaSinkPutc(ms, DirGenEntry);
|
|
metaSinkPutc(ms, 0);
|
|
metaSinkPutc(ms, 4);
|
|
metaSinkPutUint32(ms, dir->gen);
|
|
}
|
|
|
|
metaSinkEOR(ms);
|
|
}
|
|
|
|
|
|
void
|
|
plan9ToVacDir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
|
|
{
|
|
memset(vd, 0, sizeof(VacDir));
|
|
|
|
vd->elem = vtStrDup(dir->name);
|
|
vd->entry = entry;
|
|
vd->qid = qid;
|
|
vd->uid = vtStrDup(dir->uid);
|
|
vd->gid = vtStrDup(dir->gid);
|
|
vd->mid = vtStrDup(dir->muid);
|
|
vd->mtime = dir->mtime;
|
|
vd->mcount = 0;
|
|
vd->ctime = dir->mtime; /* ctime: not available on plan 9 */
|
|
vd->atime = dir->atime;
|
|
|
|
vd->mode = dir->mode & 0777;
|
|
if(dir->mode & DMDIR)
|
|
vd->mode |= ModeDir;
|
|
if(dir->mode & DMAPPEND)
|
|
vd->mode |= ModeAppend;
|
|
if(dir->mode & DMEXCL)
|
|
vd->mode |= ModeExclusive;
|
|
|
|
vd->plan9 = 1;
|
|
vd->p9path = dir->qid.path;
|
|
vd->p9version = dir->qid.vers;
|
|
}
|
|
|
|
|
|
void
|
|
metaSinkEOR(MetaSink *k)
|
|
{
|
|
uchar *p;
|
|
int o, n;
|
|
|
|
p = k->buf + MetaHeaderSize;
|
|
p += k->nindex * MetaIndexSize;
|
|
o = k->rp-k->buf; /* offset from start of block */
|
|
n = k->p-k->rp; /* size of entry */
|
|
p[0] = o >> 8;
|
|
p[1] = o;
|
|
p[2] = n >> 8;
|
|
p[3] = n;
|
|
k->rp = k->p;
|
|
k->nindex++;
|
|
if(k->nindex == k->maxindex)
|
|
metaSinkFlush(k);
|
|
}
|
|
|
|
void
|
|
metaSinkClose(MetaSink *k)
|
|
{
|
|
metaSinkFlush(k);
|
|
sinkClose(k->sink);
|
|
}
|
|
|
|
void
|
|
metaSinkFree(MetaSink *k)
|
|
{
|
|
sinkFree(k->sink);
|
|
vtMemFree(k->buf);
|
|
vtMemFree(k);
|
|
}
|
|
|
|
static void
|
|
warn(char *fmt, ...)
|
|
{
|
|
va_list arg;
|
|
|
|
va_start(arg, fmt);
|
|
fprint(2, "%s: ", argv0);
|
|
vfprint(2, fmt, arg);
|
|
fprint(2, "\n");
|
|
va_end(arg);
|
|
}
|
|
|
|
static void
|
|
cleanup(void)
|
|
{
|
|
if(oname != nil)
|
|
remove(oname);
|
|
}
|
|
|
|
#define TWID64 ((u64int)~(u64int)0)
|
|
|
|
static u64int
|
|
unittoull(char *s)
|
|
{
|
|
char *es;
|
|
u64int n;
|
|
|
|
if(s == nil)
|
|
return TWID64;
|
|
n = strtoul(s, &es, 0);
|
|
if(*es == 'k' || *es == 'K'){
|
|
n *= 1024;
|
|
es++;
|
|
}else if(*es == 'm' || *es == 'M'){
|
|
n *= 1024*1024;
|
|
es++;
|
|
}else if(*es == 'g' || *es == 'G'){
|
|
n *= 1024*1024*1024;
|
|
es++;
|
|
}
|
|
if(*es != '\0')
|
|
return TWID64;
|
|
return n;
|
|
}
|