⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vac.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
#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 + -