Example #1
0
void
main(int argc, char **argv)
{
	Avlwalk *w;
	Biobuf bout;
	Entry *e;

	quotefmtinstall();
	ARGBEGIN{
	default:
		usage();
	}ARGEND

	if(argc != 1)
		usage();

	Binit(&bout, 1, OWRITE);
	db = opendb(argv[0]);
	w = avlwalk(db->avl);
	while(e = (Entry*)avlnext(w))
		Bprint(&bout, "%q %q %luo %q %q %lud %lld\n",
			e->name, strcmp(e->name, e->d.name)==0 ? "-" : e->d.name, e->d.mode,
			e->d.uid, e->d.gid, e->d.mtime, e->d.length);
	if(Bterm(&bout) < 0)
		sysfatal("writing output: %r");

	exits(nil);
}
Example #2
0
void
main(int argc, char **argv)
{ 
	char *f[10], *local, *name, *remote, *s, *t, verb;
	int fd, havedb, havelocal, i, k, n, nf, resolve1, skip;
	int checkedmatch1, checkedmatch2, 
		checkedmatch3, checkedmatch4;
	ulong now;
	Biobuf bin;
	Dir dbd, ld, nd, rd;
	Avlwalk *w;
	Entry *e;

	membogus(argv);
	quotefmtinstall();
	ARGBEGIN{
	case 's':
	case 'c':
		i = ARGC();
		addresolve(i, EARGF(usage()));
		break;
	case 'n':
		donothing = 1;
		verbose = 1;
		break;
	case 'S':
		safeinstall = 0;
		break;
	case 'T':
		timefile = EARGF(usage());
		break;
	case 't':
		tempspool = 0;
		break;
	case 'u':
		douid = 1;
		break;
	case 'v':
		verbose++;
		break;
	default:
		usage();
	}ARGEND

	if(argc < 3)
		usage();

	if(timefile)
		readtimefile();

	lroot = argv[1];
	if(!isdir(lroot))
		sysfatal("bad local root directory");
	rroot = argv[2];
	if(!isdir(rroot))
		sysfatal("bad remote root directory");

	match = argv+3;
	nmatch = argc-3;
	for(i=0; i<nmatch; i++)
		if(match[i][0] == '/')
			match[i]++;

	if((clientdb = opendb(argv[0])) == nil)
		sysfatal("opendb %q: %r", argv[2]);
	
	copyerr = opendb(nil);

	skip = 0;
	Binit(&bin, 0, OREAD);
	for(; s=Brdstr(&bin, '\n', 1); free(s)){
		t = estrdup(s);
		nf = tokenize(s, f, nelem(f));
		if(nf != 10 || strlen(f[2]) != 1){
			skip = 1;
			fprint(2, "warning: skipping bad log entry <%s>\n", t);
			free(t);
			continue;
		}
		free(t);
		now = strtoul(f[0], 0, 0);
		n = atoi(f[1]);
		verb = f[2][0];
		name = f[3];
		if(now < maxnow || (now==maxnow && n <= maxn))
			continue;
		local = mkname(localbuf, sizeof localbuf, lroot, name);
		if(strcmp(f[4], "-") == 0)
			f[4] = f[3];
		remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
		rd.name = f[4];
		rd.mode = strtoul(f[5], 0, 8);
		rd.uid = f[6];
		rd.gid = f[7];
		rd.mtime = strtoul(f[8], 0, 10);
		rd.length = strtoll(f[9], 0, 10);
		havedb = finddb(clientdb, name, &dbd)>=0;
		havelocal = localdirstat(local, &ld)>=0;

		resolve1 = resolve(name);

		/*
		 * if(!ismatch(name)){
		 *	skip = 1;
		 *	continue;
		 * }
		 * 
		 * This check used to be right here, but we want
		 * the time to be able to move forward past entries
		 * that don't match and have already been applied.
		 * So now every path below must checked !ismatch(name)
		 * before making any changes to the local file
		 * system.  The fake variable checkedmatch
		 * tracks whether !ismatch(name) has been checked.
		 * If the compiler doesn't produce any used/set
		 * warnings, then all the paths should be okay.
		 * Even so, we have the asserts to fall back on.
		 */
		switch(verb){
		case 'd':	/* delete file */
			delce(local);
			if(!havelocal)	/* doesn't exist; who cares? */
				break;
			if(access(remote, AEXIST) >= 0)	/* got recreated! */
				break;
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch1);
			if(!havedb){
				if(resolve1 == 's')
					goto DoRemove;
				else if(resolve1 == 'c')
					goto DoRemoveDb;
				conflict(name, "locally created; will not remove");
				skip = 1;
				continue;
			}
			assert(havelocal && havedb);
			if(dbd.mtime > rd.mtime)		/* we have a newer file than what was deleted */
				break;
			if(samecontents(local, remote) > 0){	/* going to get recreated */
				chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
				break;
			}
			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* locally modified since we downloaded it */
				if(resolve1 == 's')
					goto DoRemove;
				else if(resolve1 == 'c')
					break;
				conflict(name, "locally modified; will not remove");
				skip = 1;
				continue;
			}
		    DoRemove:
			USED(checkedmatch1);
			assert(ismatch(name));
			chat("d %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
			if(donothing)
				break;
			if(remove(local) < 0){
				error("removing %q: %r", name);
				skip = 1;
				continue;
			}
		    DoRemoveDb:
			USED(checkedmatch1);
			assert(ismatch(name));
			removedb(clientdb, name);
			break;

		case 'a':	/* add file */
			if(!havedb){
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch2);
				if(!havelocal)
					goto DoCreate;
				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
					break;
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCreateDb;
				}
				if(resolve1 == 's')
					goto DoCreate;
				else if(resolve1 == 'c')
					goto DoCreateDb;
				conflict(name, "locally created; will not overwrite");
				skip = 1;
				continue;
			}
			assert(havedb);
			if(dbd.mtime >= rd.mtime)	/* already created this file; ignore */
				break;
			if(havelocal){
				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
					break;
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch2);
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCreateDb;
				}
				if(dbd.mtime==ld.mtime && dbd.length==ld.length)
					goto DoCreate;
				if(resolve1=='s')
					goto DoCreate;
				else if(resolve1 == 'c')
					goto DoCreateDb;
				conflict(name, "locally modified; will not overwrite");
				skip = 1;
				continue;
			}
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch2);
		    DoCreate:
			USED(checkedmatch2);
			assert(ismatch(name));
			if(notexists(remote)){
				addce(local);
				/* no skip=1 */
				break;;
			}
			chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
			if(donothing)
				break;
			if(rd.mode&DMDIR){
				fd = create(local, OREAD, DMDIR);
				if(fd < 0 && isdir(local))
					fd = open(local, OREAD);
				if(fd  < 0){
					error("mkdir %q: %r", name);
					skip = 1;
					continue;
				}
				nulldir(&nd);
				nd.mode = rd.mode;
				if(dirfwstat(fd, &nd) < 0)
					fprint(2, "warning: cannot set mode on %q\n", local);
				nulldir(&nd);
				nd.gid = rd.gid;
				if(dirfwstat(fd, &nd) < 0)
					fprint(2, "warning: cannot set gid on %q\n", local);
				if(douid){
					nulldir(&nd);
					nd.uid = rd.uid;
					if(dirfwstat(fd, &nd) < 0)
						fprint(2, "warning: cannot set uid on %q\n", local);
				}
				close(fd);
				rd.mtime = now;
			}else{
				if(copyfile(local, remote, name, &rd, 1, &k) < 0){
					if(k)
						addce(local);
					skip = 1;
					continue;
				}
			}
		    DoCreateDb:
			USED(checkedmatch2);
			assert(ismatch(name));
			insertdb(clientdb, name, &rd);
			break;
			
		case 'c':	/* change contents */
			if(!havedb){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch3);
				if(resolve1 == 's')
					goto DoCopy;
				else if(resolve1=='c')
					goto DoCopyDb;
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCopyDb;
				}
				if(havelocal)
					conflict(name, "locally created; will not update");
				else
					conflict(name, "not replicated; will not update");
				skip = 1;
				continue;
			}
			if(dbd.mtime >= rd.mtime)		/* already have/had this version; ignore */
				break;
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch3);
			if(!havelocal){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(resolve1 == 's')
					goto DoCopy;
				else if(resolve1 == 'c')
					break;
				conflict(name, "locally removed; will not update");
				skip = 1;
				continue;
			}
			assert(havedb && havelocal);
			if(dbd.mtime != ld.mtime || dbd.length != ld.length){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(samecontents(local, remote) > 0){
					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
					goto DoCopyDb;
				}
				if(resolve1 == 's')
					goto DoCopy;
				else if(resolve1 == 'c')
					goto DoCopyDb;
				conflict(name, "locally modified; will not update [%llud %lud -> %llud %lud]", dbd.length, dbd.mtime, ld.length, ld.mtime);
				skip = 1;
				continue;
			}
		    DoCopy:
			USED(checkedmatch3);
			assert(ismatch(name));
			if(notexists(remote)){
				addce(local);
				/* no skip=1 */
				break;
			}
			chat("c %q\n", name);
			if(donothing)
				break;
			if(copyfile(local, remote, name, &rd, 0, &k) < 0){
				if(k)
					addce(local);
				skip = 1;
				continue;
			}
		    DoCopyDb:
			USED(checkedmatch3);
			assert(ismatch(name));
			if(!havedb){
				if(havelocal)
					dbd = ld;
				else
					dbd = rd;
			}
			dbd.mtime = rd.mtime;
			dbd.length = rd.length;
			insertdb(clientdb, name, &dbd);
			break;			

		case 'm':	/* change metadata */
			if(!havedb){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's'){
					USED(checkedmatch4);
					SET(checkedmatch2);
					goto DoCreate;
				}
				else if(resolve1 == 'c')
					goto DoMetaDb;
				if(havelocal)
					conflict(name, "locally created; will not update metadata");
				else
					conflict(name, "not replicated; will not update metadata");
				skip = 1;
				continue;
			}
			if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime)		/* have newer version; ignore */
				break;
			if((dbd.mode&DMDIR) && dbd.mtime > now)
				break;
			if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)
				break;
			if(!havelocal){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's'){
					USED(checkedmatch4);
					SET(checkedmatch2);
					goto DoCreate;
				}
				else if(resolve1 == 'c')
					break;
				conflict(name, "locally removed; will not update metadata");
				skip = 1;
				continue;
			}
			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* this check might be overkill */
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's' || samecontents(local, remote) > 0)
					goto DoMeta;
				else if(resolve1 == 'c')
					break;
				conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo",
					dbd.mtime != ld.mtime ? "mtime" :
					dbd.length != ld.length ? "length" : 
					"unknown",
					rd.uid, rd.gid, rd.mode);
				skip = 1;
				continue;
			}
			if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
				if(notexists(remote)){
					addce(local);
					/* no skip=1 */
					break;
				}
				if(!ismatch(name)){
					skip = prstopped(skip, name);
					continue;
				}
				SET(checkedmatch4);
				if(resolve1 == 's')
					goto DoMeta;
				else if(resolve1 == 'c')
					break;
				conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
				skip = 1;
				continue;
			}
			if(!ismatch(name)){
				skip = prstopped(skip, name);
				continue;
			}
			SET(checkedmatch4);
		    DoMeta:
			USED(checkedmatch4);
			assert(ismatch(name));
			if(notexists(remote)){
				addce(local);
				/* no skip=1 */
				break;
			}
			chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
			if(donothing)
				break;
			nulldir(&nd);
			nd.gid = rd.gid;
			nd.mode = rd.mode;
			if(douid)
				nd.uid = rd.uid;
			if(dirwstat(local, &nd) < 0){
				error("dirwstat %q: %r", name);
				skip = 1;
				continue;
			}
		    DoMetaDb:
			USED(checkedmatch4);
			assert(ismatch(name));
			if(!havedb){
				if(havelocal)
					dbd = ld;
				else
					dbd = rd;
			}
			if(dbd.mode&DMDIR)
				dbd.mtime = now;
			dbd.gid = rd.gid;
			dbd.mode = rd.mode;
			if(douid)
				dbd.uid = rd.uid;
			insertdb(clientdb, name, &dbd);
			break;
		}
		if(!skip && !donothing){
			maxnow = now;
			maxn = n;
		}
	}

	w = avlwalk(copyerr->avl);
	while(e = (Entry*)avlnext(w))
		error("copying %q: %s\n", e->name, e->d.name);

	if(timefile)
		writetimefile();
	if(nconf)
		exits("conflicts");

	if(errors)
		exits("errors");
	exits(nil);
}