#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 noatimes; int bsize = BlockSize; int maxbsize; char *oname, *dfile, *vname; 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 *host = nil; int statsFlag = 0; atexit(cleanup); ARGBEGIN{ default: usage(); case 'A': noatimes=1; break; case 'N': vname = EARGF(usage()); break; 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 void usage(void) { fprint(2, "usage: %s [-Amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-N name] [-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, "%s: vtSha1Check: n = %d %V %V\n", argv0, 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"); 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, (vname)?vname: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>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; 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 void vacStdin(DirSink *dsink, char *name, VacFile *vf) { Dir *dir; VacDir vd; ulong entry; if(verbose) fprint(2, "%s\n", ""); dir = dirfstat(0); if(dir == nil) { warn("can't stat : %r"); return; } entry = dsink->nentry; vacData(dsink, 0, "", 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; imtime && 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 void vacDir(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; ln = smprint("%s/%s", lname, name); sn = smprint("%s/%s", sname, name); if (ln == nil || sn == nil) sysfatal("out of memory"); if(vf != nil) vvf = vfWalk(vf, name); else vvf = nil; vacFile(ds, ln, sn, vvf); if(vvf != nil) vfDecRef(vvf); free(ln); free(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 be 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, "%s: merging: %s\n", argv0, 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; ipbuf[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; ipbuf[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(; ibuf+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 = (noatimes)?dir->mtime: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; }