📄 vac.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 { Debug = 1, 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;voidmain(int argc, char *argv[]){ VtSession *z; char *host = nil; int statsFlag = 0; atexit(cleanup); ARGBEGIN{ default: usage(); case 'b': bsize = unittoull(EARGF(usage())); if(bsize == ~0) usage(); break; case 'd': dfile = EARGF(usage()); break; case 'e': if(nexclude >= MaxExclude) sysfatal("too many exclusions"); exclude[nexclude++] = EARGF(usage()); break; case 'f': oname = EARGF(usage()); break; case 'h': host = EARGF(usage()); break; case 'i': isi = EARGF(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, "%s: warning: could not ask server to flush pending writes: %R\n", argv0); if(statsFlag) fprint(2, "%s: files %ld:%ld data %ld:%ld:%ld meta %ld\n", argv0, stats.file, stats.sfile, stats.data, stats.skip, stats.sdata, stats.meta);//packetStats(); vtClose(z); vtDetach(); exits(0);}static voidusage(void){ fprint(2, "usage: %s [-amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-f vacfile] file ... \n", argv0); exits("usage");}static intstrpCmp(void *p0, void *p1){ return strcmp(*(char**)p0, *(char**)p1);}intreadBlock(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;}intvacWrite(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, "%s: vtSha1Check: n = %d %V %V\n", argv0, n, score, score2); vtSetError("vtSha1Check failed"); return 0; } return 1;}static intvac(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"); dsink = dirSinkAlloc(z, bsize, bsize); fs = nil; if(dfile != nil) { fs = vfsOpen(z, dfile, 1, 10000); if(fs == nil) fprint(2, "%s: could not open diff: %s: %s\n", argv0, 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'; if (chdir(*argv) < 0) sysfatal("can't cd to %s: %r", *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", 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 intisExcluded(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 voidvacFile(DirSink *dsink, char *lname, char *sname, VacFile *vf){ int fd; Dir *dir; Dir fake; 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()); /* * fake up dsink & vf contents so we don't explode later. * I'm not certain that this is needed, but it seems like * a wise precaution. */ entry = dsink->nentry; /* pretend it's a plain file */ dir = &fake; nulldir(dir); dir->type = 'M'; dir->dev = 10; dir->qid = (Qid){ 10, 2, QTFILE}; dir->mode = 0664; dir->atime = dir->mtime = time(nil); dir->length = 0; dir->name = sname; dir->uid = dir->gid = dir->muid = "missing"; vacData(dsink, fd, lname, vf, dir); plan9ToVacDir(&vd, dir, entry, fileid++); metaSinkWriteDir(dsink->msink, &vd); vdCleanup(&vd); 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 voidvacStdin(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 ulongvacDataSkip(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 voidvacData(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 && vd.size == dir->length && (!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) && vd.size < dir->length && vd.plan9 && 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)) 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 voidvacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf){ Dir *dirs; char *ln, *sn; char *name; int i, nd; DirSink *ds; VacFile *vvf; /* * if we could see the score underlying dir, we could quickly * short-circuit further directory descent if vf (see vacfs(vf)->score) * and dir had identical scores. */ 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 || name[0] == '/' || strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -