henesyinit from lsubb0c8bdb6 years agoHistory
507 lines · 9.3 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <bio.h>#include <ctype.h>#include <error.h>#include <b.h>#include <avl.h>#include "repl.h" #define vprint	if(verbose)print#define dprint	if(debug)print int pri;int verbose;int dryrun;int dont;int debug;int force; char**	dirdels;int	ndirdels; char**	syncdirs;int	nsyncdirs;  static voidadddirdel(char* d){	if ((ndirdels%Incr) == 0)		dirdels = erealloc(dirdels, (ndirdels+Incr)*sizeof(char*));	dirdels[ndirdels++] = estrdup(d);} static intcontained(char* fname, char* top){	int	l; 	l = strlen(top);	if (strncmp(fname, top, l) == 0)	if (fname[l] == '/' || fname[l] == 0)		return 1;	return 0;} static voidaddsyncdir(char* d){	if (d[0] == '/' || (d[0] == '.' && (d[1] == '/' || d[1] == 0))){		fprint(2, "%s: not relative from repl root.\n", d);		exits("baddir");	} 	if ((nsyncdirs%Incr) == 0)		syncdirs = erealloc(syncdirs, (nsyncdirs+Incr)*sizeof(char*));	syncdirs[nsyncdirs++] = smprint("/%s", d);}  static intmustsync(char* d){	int	i; 	if (nsyncdirs == 0)	// no dirs: sync it all.		return 1; 	for (i = 0; i < nsyncdirs; i++)		if (contained(d, syncdirs[i]))			return 1;	return 0;} static voiddeldirs(void){	int	i; 	for (i = ndirdels - 1; i >= 0; i--){		dprint("remove %s\n", dirdels[i]);		remove(dirdels[i]);	}} /* Keep in sync with scandb.c:/^cmpfile * But be very careful not to compare qid.vers here, * because qids come from different servers. */static intcmpfile(Dir* op, Dir* np){	if ((op->qid.type&QTDIR)==0){		if (op->length != np->length || op->mtime != np->mtime)			return Datachg;		if (op->mode != np->mode)			return Metachg;	}	if (op->mode != np->mode ||	    strcmp(op->uid, np->uid) || strcmp(op->gid, np->gid))		return Metachg;	return Eq;} static intdeletefile(char* to, char* fname, ulong dmtime){	char* dfile;	char* e;	Dir*  dd; 	dfile = smprint("%s%s", to, fname);	dd = dirstat(dfile);	if (dd != nil){		if (dd->mode&DMDIR){			adddirdel(dfile);			return 1;		}		if (!force && dmtime && dmtime != dd->mtime){			free(dd);			error("locally modified: %s: won't delete", dfile);		}		free(dd);	}	dprint("remove %s\n", fname);	if (remove(dfile)<0){		e = smprint("remove %s: %r\n", dfile);		if (!strstr(e, "does not exist")){			warn(e);			free(e);			return 0;		}		free(e);	}	return 1;} static voidcopytofd(int fd, char* from){	static char buf[64*1024];	int	ffd;	int	n; 	ffd = open(from, OREAD);	if (ffd < 0)		error("open %s: %r", from);	while(n = read(ffd, buf, sizeof(buf))){		if (n < 0)			error("read %s: %r", from);		if (write(fd, buf, n) != n)			error("copying %s: %r", from);	}	close(ffd);} /* * This synchronizes the files according to their real FS. * The metadata within the DB is ignored. * The worst that can happen is that a more recent file is * copied, but according to the DB the file has changed anyway. * Perhaps relying just on the DB to see what to do would simplify * this looong routine. */ static intsyncfile(char* to, char* from, char* fname, ulong dmtime, ulong* vers){	char*	dfile;	char*	tfile;	char*	sfile;	Dir*	sd;	Dir*	dd;	Dir*	td;	Dir	wd;	int	fd;	int	what;	int	omode;	char*	newfile;	int	isnew; 	dfile = smprint("%s%s", to, fname);	tfile = smprint("%s%s!tmp", to, fname);	sfile = smprint("%s%s", from, fname);	dprint("syncfile: %s: %s -> %s \n", fname, from, to);	newfile = nil;	sd = dirstat(sfile);	dd = nil;	fd = -1;	if (catcherror()){		free(dfile);		free(tfile);		free(sfile);		free(sd);		free(dd);		if (newfile)			remove(newfile);		if (fd != -1)			close(fd);		fprint(2, "%r\n");		return 0;	} 	if (sd == nil)		error("can't stat %s: %r", sfile); 	dd = dirstat(dfile);	if (dd == nil)		what = Datachg;	else {		if (!force && !(dd->mode&DMDIR) && dmtime && dd->mtime != dmtime)			error("locally modified: %s: wont update", dfile);		what = cmpfile(dd, sd);		if (what == Datachg && (dd->mode&DMAPPEND))			error("append-only file: %s: wont update", dfile);	}	if (sd->qid.type&QTDIR)		omode = OREAD;	else		omode = OWRITE|OTRUNC;	nulldir(&wd);	isnew = 0;	switch(what){	case Eq:		warn("same file: %s", dfile);		td = dirstat(dfile);		if (td != nil){			*vers = td->qid.vers;			free(td);		}		break;	case Datachg:		if (dd == nil){			fd = create(tfile, omode, 0755|(sd->mode&DMDIR));			if (fd < 0)				error("can't create %s: %r", tfile);			newfile = tfile;			isnew = 1;		} else {			fd = open(dfile, omode);			if (fd < 0)				error("open %s: %r", dfile);		}		if (!(sd->qid.type&QTDIR)){			copytofd(fd, sfile);			if (!isnew)				dprint("c %s\n", fname);		}		td = dirfstat(fd);		if (td != nil){			*vers = td->qid.vers;			free(td);		}		if (close(fd)<0)			error("close %s: %r", dfile);		if (isnew){			wd.name = sd->name;			if (dirwstat(tfile, &wd) < 0)				error("can't rename %s: %r", tfile);			nulldir(&wd);			newfile = nil;			dprint("a %s\n", fname);		}		// fall through...	case Metachg:		if (dd == nil || dd->mode != sd->mode){			wd.mode = sd->mode;			if (dirwstat(dfile, &wd) < 0)				error("can't wstat %s: %r", dfile);		}		if (!(sd->qid.type&QTDIR))		if (dd == nil || dd->mtime != sd->mtime)			wd.mtime = sd->mtime;		if (dd == nil || strcmp(dd->uid, sd->uid))			wd.uid = sd->uid;		if (dd == nil || strcmp(dd->gid, sd->gid))			wd.gid = sd->gid;		if (dirwstat(dfile, &wd) < 0)			warn("can't set mtime/uids: %s: %r", dfile);		if (!isnew)			dprint("m %s\n", fname);		break;	} 	noerror();	free(dfile);	free(tfile);	free(sfile);	free(sd);	free(dd);	if (newfile)		remove(newfile);	return 1;} static voidsyncdbent(Db* to, Dbent* tdp, Dbent* fdp, ulong vers){ 	to->changed = 1;	if (tdp == nil){		tdp = emalloc(sizeof(Dbent));		memset(tdp, 0, sizeof(Dbent));		tdp->fname = estrdup(fdp->fname);		tdp->next = to->new;		to->new = tdp;	}	tdp->hist = estrdup(fdp->hist);	tdp->hlen = fdp->hlen;	tdp->Meta = fdp->Meta;	tdp->vers = vers;} static voidaddnewents(Db* db){	Dbent*	dp; 	while(db->new != nil){		dp = db->new;		db->new = dp->next;		// dp->next = nil; not needed.		insertdb(db, dp);	}} static voidchange(Db* to, Db* from, Dbent* tdp, Dbent* fdp){	int	isdel;	ulong	dmtime;	ulong	vers; 	assert(fdp != nil);	if (dont == to->id)		return;	if (0)		vprint("%c %s %s\n", from->id, fdp->fname, fdp->hist); 	isdel = isupper(fdp->hist[fdp->hlen-1]);	dmtime = tdp ? tdp->mtime : 0L;	if (isdel){		if (tdp){			vprint("d %s%s\n", to->dir, tdp->fname);			if (!dryrun && deletefile(to->dir, tdp->fname, dmtime))				syncdbent(to, tdp, fdp, 0);		} else			syncdbent(to, nil, fdp, 0);	} else {		vprint("c %s%s\n", to->dir, fdp->fname);		if (!dryrun)		if (syncfile(to->dir, from->dir, fdp->fname, dmtime, &vers))			syncdbent(to, tdp, fdp, vers);	}	// Directories deleted in reverse order: they must be empty.	deldirs();} static voidconflict(char* fname, char* h1, char* h2){	int	i; 	print("! %s:  after [", fname);	for (i = 0; h1[i] && h2[i] && h1[i] == h2[i]; i++)		print("%c", h1[i]);	print("] either [%s] or [%s].\n", h1+i, h2+i);} static voidchanges(Db* db1, Db* db2){	Avlwalk*w;	Dbent*	dp1;	Dbent*	dp2;	int	l; 	w = avlwalk(db1->tree);	while(dp1 = (Dbent*)avlnext(w)){		if (!mustsync(dp1->fname))			continue;		dp2 = (Dbent*)lookupavl(db2->tree, (Avl*)dp1);		if (dp2 == nil){			change(db2, db1, nil, dp1);			continue;		}		dp2->visited = 1;		l = dp1->hlen;		if (dp2->hlen < l)			l = dp2->hlen;		if (strncmp(dp1->hist, dp2->hist, l) != 0){			if (pri == db1->id)				change(db2, db1, dp2, dp1);			else if (pri == db2->id)				change(db1, db2, dp1, dp2);			else				conflict(dp1->fname, dp1->hist, dp2->hist);		} else if (dp1->hlen < dp2->hlen){			change(db1, db2, dp1, dp2);		} else if (dp1->hlen > dp2->hlen){			change(db2, db1, dp2, dp1);		}	}	endwalk(w); 	w = avlwalk(db2->tree);	while(dp2 = (Dbent*)avlnext(w)){		if (!mustsync(dp2->fname))			continue;		if (!dp2->visited)			change(db1, db2, nil, dp2);	}	endwalk(w); 	// Done with avlwalks. Add any new node.	addnewents(db1);	addnewents(db2);} static voidusage(void){	fprint(2, "usage: %s [-12Dflrnv] dir1 dir2 db1 db2 [syncdir...]\n", argv0);	exits("usage");} voidmain(int argc, char **argv){	Db*	db1;	Db*	db2;	char*	nfname;	int	i;	Error	e; 	ARGBEGIN {	case '1':		pri=1;		break;	case '2':		pri=2;		break;	case 'D':		debug=1;		break;	case 'f':		force=1;		break;	case 'l':		dont=2;		break;	case 'r':		dont=1;		break;	case 'v':		verbose=1;		break;	case 'n':		dryrun=verbose=1;		break;	default:		usage();	} ARGEND; 	if (argc < 4)		usage();	errinit(&e);	if (catcherror()){		sysfatal("%r");	} 	for (i = 4; i < argc; i++)		addsyncdir(argv[i]); 	db1 = readdbfile(argv[2]);	db2 = readdbfile(argv[3]);	if (*argv[0] != '/' || *argv[1] != '/')		sysfatal("replica dirs must be absolute");	db1->dir = cleanname(estrdup(argv[0]));	db2->dir = cleanname(estrdup(argv[1]));	if (!strcmp(db1->dir, db2->dir) || db1->id == db2->id)		sysfatal("same replica"); 	if (pri == 1)		pri = db1->id;	else if (pri == 2)		pri = db2->id;	if (dont == 1)		dont = db1->id;	else if (dont == 2)		dont = db2->id; 	changes(db1, db2); 	if (db1->changed){		nfname = smprint("%s.new", argv[2]);		writedbfile(nfname, db1);		rename(argv[2], nfname);	}	if (db2->changed){

Lines 1–500 of 507.