xref: /plan9/sys/src/cmd/replica/applylog.c (revision 66d9869129ceef534e7ca537d062f37c0d2cc439)
19a747e4fSDavid du Colombier #include "all.h"
29a747e4fSDavid du Colombier 
38847fb04SDavid du Colombier #define	Nwork	16
48847fb04SDavid du Colombier 
59a747e4fSDavid du Colombier int localdirstat(char*, Dir*);
69a747e4fSDavid du Colombier int ismatch(char*);
79a747e4fSDavid du Colombier void conflict(char*, char*, ...);
89a747e4fSDavid du Colombier void error(char*, ...);
99a747e4fSDavid du Colombier int isdir(char*);
109a747e4fSDavid du Colombier 
118847fb04SDavid du Colombier void worker(int fdf, int fdt, char *from, char *to);
128847fb04SDavid du Colombier vlong	nextoff(void);
138847fb04SDavid du Colombier void	failure(void *, char *note);
148847fb04SDavid du Colombier 
158847fb04SDavid du Colombier QLock	lk;
168847fb04SDavid du Colombier vlong	off;
178847fb04SDavid du Colombier 
189a747e4fSDavid du Colombier int errors;
199a747e4fSDavid du Colombier int nconf;
209a747e4fSDavid du Colombier int donothing;
219a747e4fSDavid du Colombier int verbose;
229a747e4fSDavid du Colombier char **match;
239a747e4fSDavid du Colombier int nmatch;
246b6b9ac8SDavid du Colombier int tempspool = 1;
256b6b9ac8SDavid du Colombier int safeinstall = 1;
269a747e4fSDavid du Colombier char *lroot;
279a747e4fSDavid du Colombier char *rroot;
289a747e4fSDavid du Colombier Db *clientdb;
299a747e4fSDavid du Colombier int skip;
309a747e4fSDavid du Colombier int douid;
319a747e4fSDavid du Colombier char *mkname(char*, int, char*, char*);
329a747e4fSDavid du Colombier char localbuf[10240];
339a747e4fSDavid du Colombier char remotebuf[10240];
346b6b9ac8SDavid du Colombier int copyfile(char*, char*, char*, Dir*, int, int*);
359a747e4fSDavid du Colombier ulong maxnow;
369a747e4fSDavid du Colombier int maxn;
379a747e4fSDavid du Colombier char *timefile;
389a747e4fSDavid du Colombier int timefd;
398f5875f3SDavid du Colombier int samecontents(char*, char*);
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier Db *copyerr;
429a747e4fSDavid du Colombier 
432cca75a1SDavid du Colombier typedef struct Res Res;
442cca75a1SDavid du Colombier struct Res
452cca75a1SDavid du Colombier {
462cca75a1SDavid du Colombier 	char c;
472cca75a1SDavid du Colombier 	char *name;
482cca75a1SDavid du Colombier };
492cca75a1SDavid du Colombier 
502cca75a1SDavid du Colombier Res *res;
512cca75a1SDavid du Colombier int nres;
522cca75a1SDavid du Colombier 
532cca75a1SDavid du Colombier void
addresolve(int c,char * name)542cca75a1SDavid du Colombier addresolve(int c, char *name)
552cca75a1SDavid du Colombier {
562cca75a1SDavid du Colombier 	if(name[0] == '/')
572cca75a1SDavid du Colombier 		name++;
582cca75a1SDavid du Colombier 	res = erealloc(res, (nres+1)*sizeof res[0]);
592cca75a1SDavid du Colombier 	res[nres].c = c;
602cca75a1SDavid du Colombier 	res[nres].name = name;
612cca75a1SDavid du Colombier 	nres++;
622cca75a1SDavid du Colombier }
632cca75a1SDavid du Colombier 
642cca75a1SDavid du Colombier int
resolve(char * name)652cca75a1SDavid du Colombier resolve(char *name)
662cca75a1SDavid du Colombier {
672cca75a1SDavid du Colombier 	int i, len;
682cca75a1SDavid du Colombier 
692cca75a1SDavid du Colombier 	for(i=0; i<nres; i++){
702cca75a1SDavid du Colombier 		len = strlen(res[i].name);
710b9a5132SDavid du Colombier 		if(len == 0)
720b9a5132SDavid du Colombier 			return res[i].c;
732cca75a1SDavid du Colombier 		if(strncmp(name, res[i].name, len) == 0 && (name[len]=='/' || name[len] == 0))
742cca75a1SDavid du Colombier 			return res[i].c;
752cca75a1SDavid du Colombier 	}
762cca75a1SDavid du Colombier 	return '?';
772cca75a1SDavid du Colombier }
782cca75a1SDavid du Colombier 
799a747e4fSDavid du Colombier void
readtimefile(void)809a747e4fSDavid du Colombier readtimefile(void)
819a747e4fSDavid du Colombier {
829a747e4fSDavid du Colombier 	int n;
839a747e4fSDavid du Colombier 	char buf[24];
849a747e4fSDavid du Colombier 
859a747e4fSDavid du Colombier 	if((timefd = open(timefile, ORDWR)) < 0
869a747e4fSDavid du Colombier 	&& (timefd = create(timefile, ORDWR|OEXCL, 0666)) < 0)
879a747e4fSDavid du Colombier 		return;
889a747e4fSDavid du Colombier 
899a747e4fSDavid du Colombier 	n = readn(timefd, buf, sizeof buf);
909a747e4fSDavid du Colombier 	if(n < sizeof buf)
919a747e4fSDavid du Colombier 		return;
929a747e4fSDavid du Colombier 
939a747e4fSDavid du Colombier 	maxnow = atoi(buf);
949a747e4fSDavid du Colombier 	maxn = atoi(buf+12);
959a747e4fSDavid du Colombier }
969a747e4fSDavid du Colombier 
979a747e4fSDavid du Colombier void
writetimefile(void)989a747e4fSDavid du Colombier writetimefile(void)
999a747e4fSDavid du Colombier {
1009a747e4fSDavid du Colombier 	char buf[24+1];
1019a747e4fSDavid du Colombier 
1029a747e4fSDavid du Colombier 	snprint(buf, sizeof buf, "%11lud %11d ", maxnow, maxn);
1039a747e4fSDavid du Colombier 	pwrite(timefd, buf, 24, 0);
1049a747e4fSDavid du Colombier }
1059a747e4fSDavid du Colombier 
1069a747e4fSDavid du Colombier static void membogus(char**);
1079a747e4fSDavid du Colombier 
1089a747e4fSDavid du Colombier void
addce(char * local)1099a747e4fSDavid du Colombier addce(char *local)
1109a747e4fSDavid du Colombier {
1119a747e4fSDavid du Colombier 	char e[ERRMAX];
1129a747e4fSDavid du Colombier 	Dir d;
1139a747e4fSDavid du Colombier 
1149a747e4fSDavid du Colombier 	memset(&d, 0, sizeof d);
1159a747e4fSDavid du Colombier 	rerrstr(e, sizeof e);
1169a747e4fSDavid du Colombier 	d.name = atom(e);
1179a747e4fSDavid du Colombier 	d.uid = "";
1189a747e4fSDavid du Colombier 	d.gid = "";
1199a747e4fSDavid du Colombier 	insertdb(copyerr, atom(local), &d);
1209a747e4fSDavid du Colombier }
1219a747e4fSDavid du Colombier 
1229a747e4fSDavid du Colombier void
delce(char * local)1239a747e4fSDavid du Colombier delce(char *local)
1249a747e4fSDavid du Colombier {
1259a747e4fSDavid du Colombier 	removedb(copyerr, local);
1269a747e4fSDavid du Colombier }
1279a747e4fSDavid du Colombier 
1289a747e4fSDavid du Colombier void
chat(char * f,...)1299a747e4fSDavid du Colombier chat(char *f, ...)
1309a747e4fSDavid du Colombier {
1319a747e4fSDavid du Colombier 	Fmt fmt;
1329a747e4fSDavid du Colombier 	char buf[256];
1339a747e4fSDavid du Colombier 	va_list arg;
1349a747e4fSDavid du Colombier 
1359a747e4fSDavid du Colombier 	if(!verbose)
1369a747e4fSDavid du Colombier 		return;
1379a747e4fSDavid du Colombier 
1389a747e4fSDavid du Colombier 	fmtfdinit(&fmt, 1, buf, sizeof buf);
1399a747e4fSDavid du Colombier 	va_start(arg, f);
1409a747e4fSDavid du Colombier 	fmtvprint(&fmt, f, arg);
1419a747e4fSDavid du Colombier 	va_end(arg);
1429a747e4fSDavid du Colombier 	fmtfdflush(&fmt);
1439a747e4fSDavid du Colombier }
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier void
usage(void)1469a747e4fSDavid du Colombier usage(void)
1479a747e4fSDavid du Colombier {
1486b6b9ac8SDavid du Colombier 	fprint(2, "usage: replica/applylog [-cnSstuv] [-T timefile] clientdb clientroot serverroot [path ...]\n");
1499a747e4fSDavid du Colombier 	exits("usage");
1509a747e4fSDavid du Colombier }
1519a747e4fSDavid du Colombier 
1529a747e4fSDavid du Colombier int
notexists(char * path)1539a747e4fSDavid du Colombier notexists(char *path)
1549a747e4fSDavid du Colombier {
1559a747e4fSDavid du Colombier 	char buf[ERRMAX];
1569a747e4fSDavid du Colombier 
1579a747e4fSDavid du Colombier 	if(access(path, AEXIST) >= 0)
1589a747e4fSDavid du Colombier 		return 0;
1599a747e4fSDavid du Colombier 
1609a747e4fSDavid du Colombier 	rerrstr(buf, sizeof buf);
1619a747e4fSDavid du Colombier 	if(strstr(buf, "entry not found") || strstr(buf, "not exist"))
1629a747e4fSDavid du Colombier 		return 1;
1639a747e4fSDavid du Colombier 
1649a747e4fSDavid du Colombier 	/* some other error, like network hangup */
1659a747e4fSDavid du Colombier 	return 0;
1669a747e4fSDavid du Colombier }
1679a747e4fSDavid du Colombier 
168*66d98691SDavid du Colombier int
prstopped(int skip,char * name)169*66d98691SDavid du Colombier prstopped(int skip, char *name)
170*66d98691SDavid du Colombier {
171*66d98691SDavid du Colombier 	if(!skip) {
172*66d98691SDavid du Colombier 		fprint(2, "stopped updating log apply time because of %s\n",
173*66d98691SDavid du Colombier 			name);
174*66d98691SDavid du Colombier 		skip = 1;
175*66d98691SDavid du Colombier 	}
176*66d98691SDavid du Colombier 	return skip;
177*66d98691SDavid du Colombier }
178*66d98691SDavid du Colombier 
1799a747e4fSDavid du Colombier void
main(int argc,char ** argv)1809a747e4fSDavid du Colombier main(int argc, char **argv)
1819a747e4fSDavid du Colombier {
1829a747e4fSDavid du Colombier 	char *f[10], *local, *name, *remote, *s, *t, verb;
1832cca75a1SDavid du Colombier 	int fd, havedb, havelocal, i, k, n, nf, resolve1, skip;
1842cca75a1SDavid du Colombier 	int checkedmatch1, checkedmatch2,
1852cca75a1SDavid du Colombier 		checkedmatch3, checkedmatch4;
1869a747e4fSDavid du Colombier 	ulong now;
1879a747e4fSDavid du Colombier 	Biobuf bin;
1889a747e4fSDavid du Colombier 	Dir dbd, ld, nd, rd;
1899a747e4fSDavid du Colombier 	Avlwalk *w;
1909a747e4fSDavid du Colombier 	Entry *e;
1919a747e4fSDavid du Colombier 
1929a747e4fSDavid du Colombier 	membogus(argv);
1939a747e4fSDavid du Colombier 	quotefmtinstall();
1949a747e4fSDavid du Colombier 	ARGBEGIN{
1959a747e4fSDavid du Colombier 	case 's':
1969a747e4fSDavid du Colombier 	case 'c':
1972cca75a1SDavid du Colombier 		i = ARGC();
1982cca75a1SDavid du Colombier 		addresolve(i, EARGF(usage()));
1999a747e4fSDavid du Colombier 		break;
2009a747e4fSDavid du Colombier 	case 'n':
2019a747e4fSDavid du Colombier 		donothing = 1;
2029a747e4fSDavid du Colombier 		verbose = 1;
2039a747e4fSDavid du Colombier 		break;
2046b6b9ac8SDavid du Colombier 	case 'S':
2056b6b9ac8SDavid du Colombier 		safeinstall = 0;
2066b6b9ac8SDavid du Colombier 		break;
2079a747e4fSDavid du Colombier 	case 'T':
2089a747e4fSDavid du Colombier 		timefile = EARGF(usage());
2099a747e4fSDavid du Colombier 		break;
2109a747e4fSDavid du Colombier 	case 't':
2116b6b9ac8SDavid du Colombier 		tempspool = 0;
2129a747e4fSDavid du Colombier 		break;
2139a747e4fSDavid du Colombier 	case 'u':
2149a747e4fSDavid du Colombier 		douid = 1;
2159a747e4fSDavid du Colombier 		break;
2169a747e4fSDavid du Colombier 	case 'v':
2172cca75a1SDavid du Colombier 		verbose++;
2189a747e4fSDavid du Colombier 		break;
2199a747e4fSDavid du Colombier 	default:
2209a747e4fSDavid du Colombier 		usage();
2219a747e4fSDavid du Colombier 	}ARGEND
2229a747e4fSDavid du Colombier 
2239a747e4fSDavid du Colombier 	if(argc < 3)
2249a747e4fSDavid du Colombier 		usage();
2259a747e4fSDavid du Colombier 
2269a747e4fSDavid du Colombier 	if(timefile)
2279a747e4fSDavid du Colombier 		readtimefile();
2289a747e4fSDavid du Colombier 
2299a747e4fSDavid du Colombier 	lroot = argv[1];
2309a747e4fSDavid du Colombier 	if(!isdir(lroot))
2319a747e4fSDavid du Colombier 		sysfatal("bad local root directory");
2329a747e4fSDavid du Colombier 	rroot = argv[2];
2339a747e4fSDavid du Colombier 	if(!isdir(rroot))
2349a747e4fSDavid du Colombier 		sysfatal("bad remote root directory");
2359a747e4fSDavid du Colombier 
2369a747e4fSDavid du Colombier 	match = argv+3;
2379a747e4fSDavid du Colombier 	nmatch = argc-3;
2382cca75a1SDavid du Colombier 	for(i=0; i<nmatch; i++)
2392cca75a1SDavid du Colombier 		if(match[i][0] == '/')
2402cca75a1SDavid du Colombier 			match[i]++;
2412cca75a1SDavid du Colombier 
2422cca75a1SDavid du Colombier 	if((clientdb = opendb(argv[0])) == nil)
2432cca75a1SDavid du Colombier 		sysfatal("opendb %q: %r", argv[2]);
2449a747e4fSDavid du Colombier 
2459a747e4fSDavid du Colombier 	copyerr = opendb(nil);
2469a747e4fSDavid du Colombier 
2479a747e4fSDavid du Colombier 	skip = 0;
2489a747e4fSDavid du Colombier 	Binit(&bin, 0, OREAD);
2499a747e4fSDavid du Colombier 	for(; s=Brdstr(&bin, '\n', 1); free(s)){
2509a747e4fSDavid du Colombier 		t = estrdup(s);
2519a747e4fSDavid du Colombier 		nf = tokenize(s, f, nelem(f));
2529a747e4fSDavid du Colombier 		if(nf != 10 || strlen(f[2]) != 1){
2539a747e4fSDavid du Colombier 			skip = 1;
2549a747e4fSDavid du Colombier 			fprint(2, "warning: skipping bad log entry <%s>\n", t);
2559a747e4fSDavid du Colombier 			free(t);
2569a747e4fSDavid du Colombier 			continue;
2579a747e4fSDavid du Colombier 		}
2589a747e4fSDavid du Colombier 		free(t);
2599a747e4fSDavid du Colombier 		now = strtoul(f[0], 0, 0);
2609a747e4fSDavid du Colombier 		n = atoi(f[1]);
2619a747e4fSDavid du Colombier 		verb = f[2][0];
2629a747e4fSDavid du Colombier 		name = f[3];
2639a747e4fSDavid du Colombier 		if(now < maxnow || (now==maxnow && n <= maxn))
2649a747e4fSDavid du Colombier 			continue;
2659a747e4fSDavid du Colombier 		local = mkname(localbuf, sizeof localbuf, lroot, name);
2669a747e4fSDavid du Colombier 		if(strcmp(f[4], "-") == 0)
2679a747e4fSDavid du Colombier 			f[4] = f[3];
2689a747e4fSDavid du Colombier 		remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
2699a747e4fSDavid du Colombier 		rd.name = f[4];
2709a747e4fSDavid du Colombier 		rd.mode = strtoul(f[5], 0, 8);
2719a747e4fSDavid du Colombier 		rd.uid = f[6];
2729a747e4fSDavid du Colombier 		rd.gid = f[7];
2739a747e4fSDavid du Colombier 		rd.mtime = strtoul(f[8], 0, 10);
2749a747e4fSDavid du Colombier 		rd.length = strtoll(f[9], 0, 10);
2759a747e4fSDavid du Colombier 		havedb = finddb(clientdb, name, &dbd)>=0;
2769a747e4fSDavid du Colombier 		havelocal = localdirstat(local, &ld)>=0;
2779a747e4fSDavid du Colombier 
2782cca75a1SDavid du Colombier 		resolve1 = resolve(name);
2792cca75a1SDavid du Colombier 
2802cca75a1SDavid du Colombier 		/*
2812cca75a1SDavid du Colombier 		 * if(!ismatch(name)){
2822cca75a1SDavid du Colombier 		 *	skip = 1;
2832cca75a1SDavid du Colombier 		 *	continue;
2842cca75a1SDavid du Colombier 		 * }
2852cca75a1SDavid du Colombier 		 *
2862cca75a1SDavid du Colombier 		 * This check used to be right here, but we want
2872cca75a1SDavid du Colombier 		 * the time to be able to move forward past entries
2882cca75a1SDavid du Colombier 		 * that don't match and have already been applied.
2892cca75a1SDavid du Colombier 		 * So now every path below must checked !ismatch(name)
2902cca75a1SDavid du Colombier 		 * before making any changes to the local file
2912cca75a1SDavid du Colombier 		 * system.  The fake variable checkedmatch
2922cca75a1SDavid du Colombier 		 * tracks whether !ismatch(name) has been checked.
2932cca75a1SDavid du Colombier 		 * If the compiler doesn't produce any used/set
2942cca75a1SDavid du Colombier 		 * warnings, then all the paths should be okay.
2952cca75a1SDavid du Colombier 		 * Even so, we have the asserts to fall back on.
2962cca75a1SDavid du Colombier 		 */
2979a747e4fSDavid du Colombier 		switch(verb){
2989a747e4fSDavid du Colombier 		case 'd':	/* delete file */
2999a747e4fSDavid du Colombier 			delce(local);
3009a747e4fSDavid du Colombier 			if(!havelocal)	/* doesn't exist; who cares? */
3019a747e4fSDavid du Colombier 				break;
3028f5875f3SDavid du Colombier 			if(access(remote, AEXIST) >= 0)	/* got recreated! */
3038f5875f3SDavid du Colombier 				break;
3042cca75a1SDavid du Colombier 			if(!ismatch(name)){
305*66d98691SDavid du Colombier 				skip = prstopped(skip, name);
3062cca75a1SDavid du Colombier 				continue;
3072cca75a1SDavid du Colombier 			}
3082cca75a1SDavid du Colombier 			SET(checkedmatch1);
3099a747e4fSDavid du Colombier 			if(!havedb){
3102cca75a1SDavid du Colombier 				if(resolve1 == 's')
3119a747e4fSDavid du Colombier 					goto DoRemove;
3122cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
3139a747e4fSDavid du Colombier 					goto DoRemoveDb;
3149a747e4fSDavid du Colombier 				conflict(name, "locally created; will not remove");
3159a747e4fSDavid du Colombier 				skip = 1;
3169a747e4fSDavid du Colombier 				continue;
3179a747e4fSDavid du Colombier 			}
3189a747e4fSDavid du Colombier 			assert(havelocal && havedb);
3199a747e4fSDavid du Colombier 			if(dbd.mtime > rd.mtime)		/* we have a newer file than what was deleted */
3209a747e4fSDavid du Colombier 				break;
3218f5875f3SDavid du Colombier 			if(samecontents(local, remote) > 0){	/* going to get recreated */
3228f5875f3SDavid du Colombier 				chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
3238f5875f3SDavid du Colombier 				break;
3248f5875f3SDavid du Colombier 			}
3259a747e4fSDavid du Colombier 			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* locally modified since we downloaded it */
3262cca75a1SDavid du Colombier 				if(resolve1 == 's')
3279a747e4fSDavid du Colombier 					goto DoRemove;
3282cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
3299a747e4fSDavid du Colombier 					break;
3309a747e4fSDavid du Colombier 				conflict(name, "locally modified; will not remove");
3319a747e4fSDavid du Colombier 				skip = 1;
3329a747e4fSDavid du Colombier 				continue;
3339a747e4fSDavid du Colombier 			}
3349a747e4fSDavid du Colombier 		    DoRemove:
3352cca75a1SDavid du Colombier 			USED(checkedmatch1);
3362cca75a1SDavid du Colombier 			assert(ismatch(name));
33702169c28SDavid du Colombier 			chat("d %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
3389a747e4fSDavid du Colombier 			if(donothing)
3399a747e4fSDavid du Colombier 				break;
3409a747e4fSDavid du Colombier 			if(remove(local) < 0){
3418f5875f3SDavid du Colombier 				error("removing %q: %r", name);
3429a747e4fSDavid du Colombier 				skip = 1;
3439a747e4fSDavid du Colombier 				continue;
3449a747e4fSDavid du Colombier 			}
3459a747e4fSDavid du Colombier 		    DoRemoveDb:
3462cca75a1SDavid du Colombier 			USED(checkedmatch1);
3472cca75a1SDavid du Colombier 			assert(ismatch(name));
3489a747e4fSDavid du Colombier 			removedb(clientdb, name);
3499a747e4fSDavid du Colombier 			break;
3509a747e4fSDavid du Colombier 
3519a747e4fSDavid du Colombier 		case 'a':	/* add file */
3529a747e4fSDavid du Colombier 			if(!havedb){
3532cca75a1SDavid du Colombier 				if(!ismatch(name)){
354*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
3552cca75a1SDavid du Colombier 					continue;
3562cca75a1SDavid du Colombier 				}
3572cca75a1SDavid du Colombier 				SET(checkedmatch2);
3589a747e4fSDavid du Colombier 				if(!havelocal)
3599a747e4fSDavid du Colombier 					goto DoCreate;
3609a747e4fSDavid du Colombier 				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
3619a747e4fSDavid du Colombier 					break;
3628f5875f3SDavid du Colombier 				if(samecontents(local, remote) > 0){
3638f5875f3SDavid du Colombier 					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
3648f5875f3SDavid du Colombier 					goto DoCreateDb;
3658f5875f3SDavid du Colombier 				}
3662cca75a1SDavid du Colombier 				if(resolve1 == 's')
3679a747e4fSDavid du Colombier 					goto DoCreate;
3682cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
3699a747e4fSDavid du Colombier 					goto DoCreateDb;
3709a747e4fSDavid du Colombier 				conflict(name, "locally created; will not overwrite");
3719a747e4fSDavid du Colombier 				skip = 1;
3729a747e4fSDavid du Colombier 				continue;
3739a747e4fSDavid du Colombier 			}
3749a747e4fSDavid du Colombier 			assert(havedb);
3759a747e4fSDavid du Colombier 			if(dbd.mtime >= rd.mtime)	/* already created this file; ignore */
3769a747e4fSDavid du Colombier 				break;
3779a747e4fSDavid du Colombier 			if(havelocal){
3781118d624SDavid du Colombier 				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
3791118d624SDavid du Colombier 					break;
3802cca75a1SDavid du Colombier 				if(!ismatch(name)){
381*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
3822cca75a1SDavid du Colombier 					continue;
3832cca75a1SDavid du Colombier 				}
3842cca75a1SDavid du Colombier 				SET(checkedmatch2);
3858f5875f3SDavid du Colombier 				if(samecontents(local, remote) > 0){
3868f5875f3SDavid du Colombier 					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
3878f5875f3SDavid du Colombier 					goto DoCreateDb;
3888f5875f3SDavid du Colombier 				}
3899a747e4fSDavid du Colombier 				if(dbd.mtime==ld.mtime && dbd.length==ld.length)
3909a747e4fSDavid du Colombier 					goto DoCreate;
3912cca75a1SDavid du Colombier 				if(resolve1=='s')
3929a747e4fSDavid du Colombier 					goto DoCreate;
3932cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
394*66d98691SDavid du Colombier 					goto DoCreateDb;
3959a747e4fSDavid du Colombier 				conflict(name, "locally modified; will not overwrite");
3969a747e4fSDavid du Colombier 				skip = 1;
3979a747e4fSDavid du Colombier 				continue;
3989a747e4fSDavid du Colombier 			}
3992cca75a1SDavid du Colombier 			if(!ismatch(name)){
400*66d98691SDavid du Colombier 				skip = prstopped(skip, name);
4012cca75a1SDavid du Colombier 				continue;
4022cca75a1SDavid du Colombier 			}
4032cca75a1SDavid du Colombier 			SET(checkedmatch2);
4049a747e4fSDavid du Colombier 		    DoCreate:
4052cca75a1SDavid du Colombier 			USED(checkedmatch2);
4062cca75a1SDavid du Colombier 			assert(ismatch(name));
4079a747e4fSDavid du Colombier 			if(notexists(remote)){
4089a747e4fSDavid du Colombier 				addce(local);
4099a747e4fSDavid du Colombier 				/* no skip=1 */
4109a747e4fSDavid du Colombier 				break;;
4119a747e4fSDavid du Colombier 			}
4129a747e4fSDavid du Colombier 			chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
4139a747e4fSDavid du Colombier 			if(donothing)
4149a747e4fSDavid du Colombier 				break;
4159a747e4fSDavid du Colombier 			if(rd.mode&DMDIR){
416666b04c8SDavid du Colombier 				fd = create(local, OREAD, DMDIR);
417666b04c8SDavid du Colombier 				if(fd < 0 && isdir(local))
418666b04c8SDavid du Colombier 					fd = open(local, OREAD);
419666b04c8SDavid du Colombier 				if(fd  < 0){
4209a747e4fSDavid du Colombier 					error("mkdir %q: %r", name);
4219a747e4fSDavid du Colombier 					skip = 1;
4229a747e4fSDavid du Colombier 					continue;
4239a747e4fSDavid du Colombier 				}
4249a747e4fSDavid du Colombier 				nulldir(&nd);
4259a747e4fSDavid du Colombier 				nd.mode = rd.mode;
4269a747e4fSDavid du Colombier 				if(dirfwstat(fd, &nd) < 0)
4279a747e4fSDavid du Colombier 					fprint(2, "warning: cannot set mode on %q\n", local);
4289a747e4fSDavid du Colombier 				nulldir(&nd);
4299a747e4fSDavid du Colombier 				nd.gid = rd.gid;
4309a747e4fSDavid du Colombier 				if(dirfwstat(fd, &nd) < 0)
4319a747e4fSDavid du Colombier 					fprint(2, "warning: cannot set gid on %q\n", local);
4329a747e4fSDavid du Colombier 				if(douid){
4339a747e4fSDavid du Colombier 					nulldir(&nd);
4349a747e4fSDavid du Colombier 					nd.uid = rd.uid;
4359a747e4fSDavid du Colombier 					if(dirfwstat(fd, &nd) < 0)
4369a747e4fSDavid du Colombier 						fprint(2, "warning: cannot set uid on %q\n", local);
4379a747e4fSDavid du Colombier 				}
4389a747e4fSDavid du Colombier 				close(fd);
4399a747e4fSDavid du Colombier 				rd.mtime = now;
4409a747e4fSDavid du Colombier 			}else{
4416b6b9ac8SDavid du Colombier 				if(copyfile(local, remote, name, &rd, 1, &k) < 0){
4429a747e4fSDavid du Colombier 					if(k)
4439a747e4fSDavid du Colombier 						addce(local);
4449a747e4fSDavid du Colombier 					skip = 1;
4459a747e4fSDavid du Colombier 					continue;
4469a747e4fSDavid du Colombier 				}
4479a747e4fSDavid du Colombier 			}
4489a747e4fSDavid du Colombier 		    DoCreateDb:
4492cca75a1SDavid du Colombier 			USED(checkedmatch2);
4502cca75a1SDavid du Colombier 			assert(ismatch(name));
4519a747e4fSDavid du Colombier 			insertdb(clientdb, name, &rd);
4529a747e4fSDavid du Colombier 			break;
4539a747e4fSDavid du Colombier 
4549a747e4fSDavid du Colombier 		case 'c':	/* change contents */
4559a747e4fSDavid du Colombier 			if(!havedb){
4569a747e4fSDavid du Colombier 				if(notexists(remote)){
4579a747e4fSDavid du Colombier 					addce(local);
4589a747e4fSDavid du Colombier 					/* no skip=1 */
4599a747e4fSDavid du Colombier 					break;
4609a747e4fSDavid du Colombier 				}
4612cca75a1SDavid du Colombier 				if(!ismatch(name)){
462*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
4632cca75a1SDavid du Colombier 					continue;
4642cca75a1SDavid du Colombier 				}
4652cca75a1SDavid du Colombier 				SET(checkedmatch3);
4662cca75a1SDavid du Colombier 				if(resolve1 == 's')
4679a747e4fSDavid du Colombier 					goto DoCopy;
4682cca75a1SDavid du Colombier 				else if(resolve1=='c')
4699a747e4fSDavid du Colombier 					goto DoCopyDb;
4708f5875f3SDavid du Colombier 				if(samecontents(local, remote) > 0){
4718f5875f3SDavid du Colombier 					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
4728f5875f3SDavid du Colombier 					goto DoCopyDb;
4738f5875f3SDavid du Colombier 				}
4749a747e4fSDavid du Colombier 				if(havelocal)
4759a747e4fSDavid du Colombier 					conflict(name, "locally created; will not update");
4769a747e4fSDavid du Colombier 				else
4779a747e4fSDavid du Colombier 					conflict(name, "not replicated; will not update");
4789a747e4fSDavid du Colombier 				skip = 1;
4799a747e4fSDavid du Colombier 				continue;
4809a747e4fSDavid du Colombier 			}
4819a747e4fSDavid du Colombier 			if(dbd.mtime >= rd.mtime)		/* already have/had this version; ignore */
4829a747e4fSDavid du Colombier 				break;
4832cca75a1SDavid du Colombier 			if(!ismatch(name)){
484*66d98691SDavid du Colombier 				skip = prstopped(skip, name);
4852cca75a1SDavid du Colombier 				continue;
4862cca75a1SDavid du Colombier 			}
4872cca75a1SDavid du Colombier 			SET(checkedmatch3);
4889a747e4fSDavid du Colombier 			if(!havelocal){
4899a747e4fSDavid du Colombier 				if(notexists(remote)){
4909a747e4fSDavid du Colombier 					addce(local);
4919a747e4fSDavid du Colombier 					/* no skip=1 */
4929a747e4fSDavid du Colombier 					break;
4939a747e4fSDavid du Colombier 				}
4942cca75a1SDavid du Colombier 				if(resolve1 == 's')
4959a747e4fSDavid du Colombier 					goto DoCopy;
4962cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
4979a747e4fSDavid du Colombier 					break;
4989a747e4fSDavid du Colombier 				conflict(name, "locally removed; will not update");
4999a747e4fSDavid du Colombier 				skip = 1;
5009a747e4fSDavid du Colombier 				continue;
5019a747e4fSDavid du Colombier 			}
5029a747e4fSDavid du Colombier 			assert(havedb && havelocal);
5039a747e4fSDavid du Colombier 			if(dbd.mtime != ld.mtime || dbd.length != ld.length){
5049a747e4fSDavid du Colombier 				if(notexists(remote)){
5059a747e4fSDavid du Colombier 					addce(local);
5069a747e4fSDavid du Colombier 					/* no skip=1 */
5079a747e4fSDavid du Colombier 					break;
5089a747e4fSDavid du Colombier 				}
5098f5875f3SDavid du Colombier 				if(samecontents(local, remote) > 0){
5108f5875f3SDavid du Colombier 					chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
5118f5875f3SDavid du Colombier 					goto DoCopyDb;
5128f5875f3SDavid du Colombier 				}
5132cca75a1SDavid du Colombier 				if(resolve1 == 's')
5149a747e4fSDavid du Colombier 					goto DoCopy;
5152cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
516*66d98691SDavid du Colombier 					goto DoCopyDb;
5178f5875f3SDavid du Colombier 				conflict(name, "locally modified; will not update [%llud %lud -> %llud %lud]", dbd.length, dbd.mtime, ld.length, ld.mtime);
5189a747e4fSDavid du Colombier 				skip = 1;
5199a747e4fSDavid du Colombier 				continue;
5209a747e4fSDavid du Colombier 			}
5219a747e4fSDavid du Colombier 		    DoCopy:
5222cca75a1SDavid du Colombier 			USED(checkedmatch3);
5232cca75a1SDavid du Colombier 			assert(ismatch(name));
5249a747e4fSDavid du Colombier 			if(notexists(remote)){
5259a747e4fSDavid du Colombier 				addce(local);
5269a747e4fSDavid du Colombier 				/* no skip=1 */
5279a747e4fSDavid du Colombier 				break;
5289a747e4fSDavid du Colombier 			}
5299a747e4fSDavid du Colombier 			chat("c %q\n", name);
5309a747e4fSDavid du Colombier 			if(donothing)
5319a747e4fSDavid du Colombier 				break;
5326b6b9ac8SDavid du Colombier 			if(copyfile(local, remote, name, &rd, 0, &k) < 0){
5339a747e4fSDavid du Colombier 				if(k)
5349a747e4fSDavid du Colombier 					addce(local);
5359a747e4fSDavid du Colombier 				skip = 1;
5369a747e4fSDavid du Colombier 				continue;
5379a747e4fSDavid du Colombier 			}
5389a747e4fSDavid du Colombier 		    DoCopyDb:
5392cca75a1SDavid du Colombier 			USED(checkedmatch3);
5402cca75a1SDavid du Colombier 			assert(ismatch(name));
5419a747e4fSDavid du Colombier 			if(!havedb){
5429a747e4fSDavid du Colombier 				if(havelocal)
5439a747e4fSDavid du Colombier 					dbd = ld;
5449a747e4fSDavid du Colombier 				else
5459a747e4fSDavid du Colombier 					dbd = rd;
5469a747e4fSDavid du Colombier 			}
5479a747e4fSDavid du Colombier 			dbd.mtime = rd.mtime;
5489a747e4fSDavid du Colombier 			dbd.length = rd.length;
5499a747e4fSDavid du Colombier 			insertdb(clientdb, name, &dbd);
5509a747e4fSDavid du Colombier 			break;
5519a747e4fSDavid du Colombier 
5529a747e4fSDavid du Colombier 		case 'm':	/* change metadata */
5539a747e4fSDavid du Colombier 			if(!havedb){
5549a747e4fSDavid du Colombier 				if(notexists(remote)){
5559a747e4fSDavid du Colombier 					addce(local);
5569a747e4fSDavid du Colombier 					/* no skip=1 */
5579a747e4fSDavid du Colombier 					break;
5589a747e4fSDavid du Colombier 				}
5592cca75a1SDavid du Colombier 				if(!ismatch(name)){
560*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
5612cca75a1SDavid du Colombier 					continue;
5622cca75a1SDavid du Colombier 				}
5632cca75a1SDavid du Colombier 				SET(checkedmatch4);
5642cca75a1SDavid du Colombier 				if(resolve1 == 's'){
5652cca75a1SDavid du Colombier 					USED(checkedmatch4);
5662cca75a1SDavid du Colombier 					SET(checkedmatch2);
5679a747e4fSDavid du Colombier 					goto DoCreate;
5682cca75a1SDavid du Colombier 				}
5692cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
5709a747e4fSDavid du Colombier 					goto DoMetaDb;
5719a747e4fSDavid du Colombier 				if(havelocal)
5729a747e4fSDavid du Colombier 					conflict(name, "locally created; will not update metadata");
5739a747e4fSDavid du Colombier 				else
5749a747e4fSDavid du Colombier 					conflict(name, "not replicated; will not update metadata");
5759a747e4fSDavid du Colombier 				skip = 1;
5769a747e4fSDavid du Colombier 				continue;
5779a747e4fSDavid du Colombier 			}
5789a747e4fSDavid du Colombier 			if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime)		/* have newer version; ignore */
5799a747e4fSDavid du Colombier 				break;
5809a747e4fSDavid du Colombier 			if((dbd.mode&DMDIR) && dbd.mtime > now)
5819a747e4fSDavid du Colombier 				break;
5822cca75a1SDavid du Colombier 			if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)
5832cca75a1SDavid du Colombier 				break;
5849a747e4fSDavid du Colombier 			if(!havelocal){
5859a747e4fSDavid du Colombier 				if(notexists(remote)){
5869a747e4fSDavid du Colombier 					addce(local);
5879a747e4fSDavid du Colombier 					/* no skip=1 */
5889a747e4fSDavid du Colombier 					break;
5899a747e4fSDavid du Colombier 				}
5902cca75a1SDavid du Colombier 				if(!ismatch(name)){
591*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
5922cca75a1SDavid du Colombier 					continue;
5932cca75a1SDavid du Colombier 				}
5942cca75a1SDavid du Colombier 				SET(checkedmatch4);
5952cca75a1SDavid du Colombier 				if(resolve1 == 's'){
5962cca75a1SDavid du Colombier 					USED(checkedmatch4);
5972cca75a1SDavid du Colombier 					SET(checkedmatch2);
5989a747e4fSDavid du Colombier 					goto DoCreate;
5992cca75a1SDavid du Colombier 				}
6002cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
6019a747e4fSDavid du Colombier 					break;
6029a747e4fSDavid du Colombier 				conflict(name, "locally removed; will not update metadata");
6039a747e4fSDavid du Colombier 				skip = 1;
6049a747e4fSDavid du Colombier 				continue;
6059a747e4fSDavid du Colombier 			}
6069a747e4fSDavid du Colombier 			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* this check might be overkill */
6079a747e4fSDavid du Colombier 				if(notexists(remote)){
6089a747e4fSDavid du Colombier 					addce(local);
6099a747e4fSDavid du Colombier 					/* no skip=1 */
6109a747e4fSDavid du Colombier 					break;
6119a747e4fSDavid du Colombier 				}
6122cca75a1SDavid du Colombier 				if(!ismatch(name)){
613*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
6142cca75a1SDavid du Colombier 					continue;
6152cca75a1SDavid du Colombier 				}
6162cca75a1SDavid du Colombier 				SET(checkedmatch4);
6178f5875f3SDavid du Colombier 				if(resolve1 == 's' || samecontents(local, remote) > 0)
6189a747e4fSDavid du Colombier 					goto DoMeta;
6192cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
6209a747e4fSDavid du Colombier 					break;
6212cca75a1SDavid du Colombier 				conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo",
6222cca75a1SDavid du Colombier 					dbd.mtime != ld.mtime ? "mtime" :
6232cca75a1SDavid du Colombier 					dbd.length != ld.length ? "length" :
6242cca75a1SDavid du Colombier 					"unknown",
6259a747e4fSDavid du Colombier 					rd.uid, rd.gid, rd.mode);
6269a747e4fSDavid du Colombier 				skip = 1;
6279a747e4fSDavid du Colombier 				continue;
6289a747e4fSDavid du Colombier 			}
6299a747e4fSDavid du Colombier 			if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
6309a747e4fSDavid du Colombier 				if(notexists(remote)){
6319a747e4fSDavid du Colombier 					addce(local);
6329a747e4fSDavid du Colombier 					/* no skip=1 */
6339a747e4fSDavid du Colombier 					break;
6349a747e4fSDavid du Colombier 				}
6352cca75a1SDavid du Colombier 				if(!ismatch(name)){
636*66d98691SDavid du Colombier 					skip = prstopped(skip, name);
6372cca75a1SDavid du Colombier 					continue;
6382cca75a1SDavid du Colombier 				}
6392cca75a1SDavid du Colombier 				SET(checkedmatch4);
6402cca75a1SDavid du Colombier 				if(resolve1 == 's')
6419a747e4fSDavid du Colombier 					goto DoMeta;
6422cca75a1SDavid du Colombier 				else if(resolve1 == 'c')
6439a747e4fSDavid du Colombier 					break;
6449a747e4fSDavid du Colombier 				conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
6459a747e4fSDavid du Colombier 				skip = 1;
6469a747e4fSDavid du Colombier 				continue;
6479a747e4fSDavid du Colombier 			}
6482cca75a1SDavid du Colombier 			if(!ismatch(name)){
649*66d98691SDavid du Colombier 				skip = prstopped(skip, name);
6502cca75a1SDavid du Colombier 				continue;
6512cca75a1SDavid du Colombier 			}
6522cca75a1SDavid du Colombier 			SET(checkedmatch4);
6539a747e4fSDavid du Colombier 		    DoMeta:
6542cca75a1SDavid du Colombier 			USED(checkedmatch4);
6552cca75a1SDavid du Colombier 			assert(ismatch(name));
6569a747e4fSDavid du Colombier 			if(notexists(remote)){
6579a747e4fSDavid du Colombier 				addce(local);
6589a747e4fSDavid du Colombier 				/* no skip=1 */
6599a747e4fSDavid du Colombier 				break;
6609a747e4fSDavid du Colombier 			}
6619a747e4fSDavid du Colombier 			chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
6629a747e4fSDavid du Colombier 			if(donothing)
6639a747e4fSDavid du Colombier 				break;
6649a747e4fSDavid du Colombier 			nulldir(&nd);
6659a747e4fSDavid du Colombier 			nd.gid = rd.gid;
6669a747e4fSDavid du Colombier 			nd.mode = rd.mode;
6679a747e4fSDavid du Colombier 			if(douid)
6689a747e4fSDavid du Colombier 				nd.uid = rd.uid;
6699a747e4fSDavid du Colombier 			if(dirwstat(local, &nd) < 0){
6709a747e4fSDavid du Colombier 				error("dirwstat %q: %r", name);
6719a747e4fSDavid du Colombier 				skip = 1;
6729a747e4fSDavid du Colombier 				continue;
6739a747e4fSDavid du Colombier 			}
6749a747e4fSDavid du Colombier 		    DoMetaDb:
6752cca75a1SDavid du Colombier 			USED(checkedmatch4);
6762cca75a1SDavid du Colombier 			assert(ismatch(name));
6779c14f795SDavid du Colombier 			if(!havedb){
6789c14f795SDavid du Colombier 				if(havelocal)
6799c14f795SDavid du Colombier 					dbd = ld;
6809c14f795SDavid du Colombier 				else
6819c14f795SDavid du Colombier 					dbd = rd;
6829c14f795SDavid du Colombier 			}
6839a747e4fSDavid du Colombier 			if(dbd.mode&DMDIR)
6849a747e4fSDavid du Colombier 				dbd.mtime = now;
6859a747e4fSDavid du Colombier 			dbd.gid = rd.gid;
6869a747e4fSDavid du Colombier 			dbd.mode = rd.mode;
6879a747e4fSDavid du Colombier 			if(douid)
6889a747e4fSDavid du Colombier 				dbd.uid = rd.uid;
6899a747e4fSDavid du Colombier 			insertdb(clientdb, name, &dbd);
6909a747e4fSDavid du Colombier 			break;
6919a747e4fSDavid du Colombier 		}
6929a747e4fSDavid du Colombier 		if(!skip && !donothing){
6939a747e4fSDavid du Colombier 			maxnow = now;
6949a747e4fSDavid du Colombier 			maxn = n;
6959a747e4fSDavid du Colombier 		}
6969a747e4fSDavid du Colombier 	}
6979a747e4fSDavid du Colombier 
6989a747e4fSDavid du Colombier 	w = avlwalk(copyerr->avl);
6999a747e4fSDavid du Colombier 	while(e = (Entry*)avlnext(w))
7009a747e4fSDavid du Colombier 		error("copying %q: %s\n", e->name, e->d.name);
7019a747e4fSDavid du Colombier 
7029a747e4fSDavid du Colombier 	if(timefile)
7039a747e4fSDavid du Colombier 		writetimefile();
7049a747e4fSDavid du Colombier 	if(nconf)
7059a747e4fSDavid du Colombier 		exits("conflicts");
7069a747e4fSDavid du Colombier 
7079a747e4fSDavid du Colombier 	if(errors)
7089a747e4fSDavid du Colombier 		exits("errors");
7099a747e4fSDavid du Colombier 	exits(nil);
7109a747e4fSDavid du Colombier }
7119a747e4fSDavid du Colombier 
7129a747e4fSDavid du Colombier 
7139a747e4fSDavid du Colombier char*
mkname(char * buf,int nbuf,char * a,char * b)7149a747e4fSDavid du Colombier mkname(char *buf, int nbuf, char *a, char *b)
7159a747e4fSDavid du Colombier {
7169a747e4fSDavid du Colombier 	if(strlen(a)+strlen(b)+2 > nbuf)
7179a747e4fSDavid du Colombier 		sysfatal("name too long");
7189a747e4fSDavid du Colombier 
7199a747e4fSDavid du Colombier 	strcpy(buf, a);
7209a747e4fSDavid du Colombier 	if(a[strlen(a)-1] != '/')
7219a747e4fSDavid du Colombier 		strcat(buf, "/");
7229a747e4fSDavid du Colombier 	strcat(buf, b);
7239a747e4fSDavid du Colombier 	return buf;
7249a747e4fSDavid du Colombier }
7259a747e4fSDavid du Colombier 
7269a747e4fSDavid du Colombier int
isdir(char * s)7279a747e4fSDavid du Colombier isdir(char *s)
7289a747e4fSDavid du Colombier {
7299a747e4fSDavid du Colombier 	ulong m;
7309a747e4fSDavid du Colombier 	Dir *d;
7319a747e4fSDavid du Colombier 
7329a747e4fSDavid du Colombier 	if((d = dirstat(s)) == nil)
7339a747e4fSDavid du Colombier 		return 0;
7349a747e4fSDavid du Colombier 	m = d->mode;
7359a747e4fSDavid du Colombier 	free(d);
7369a747e4fSDavid du Colombier 	return (m&DMDIR) != 0;
7379a747e4fSDavid du Colombier }
7389a747e4fSDavid du Colombier 
7399a747e4fSDavid du Colombier void
conflict(char * name,char * f,...)7409a747e4fSDavid du Colombier conflict(char *name, char *f, ...)
7419a747e4fSDavid du Colombier {
7429a747e4fSDavid du Colombier 	char *s;
7439a747e4fSDavid du Colombier 	va_list arg;
7449a747e4fSDavid du Colombier 
7459a747e4fSDavid du Colombier 	va_start(arg, f);
7469a747e4fSDavid du Colombier 	s = vsmprint(f, arg);
7479a747e4fSDavid du Colombier 	va_end(arg);
7489a747e4fSDavid du Colombier 
7498f5875f3SDavid du Colombier 	fprint(2, "! %s: %s\n", name, s);
7509a747e4fSDavid du Colombier 	free(s);
7519a747e4fSDavid du Colombier 
7529a747e4fSDavid du Colombier 	nconf++;
7539a747e4fSDavid du Colombier }
7549a747e4fSDavid du Colombier 
7559a747e4fSDavid du Colombier void
error(char * f,...)7569a747e4fSDavid du Colombier error(char *f, ...)
7579a747e4fSDavid du Colombier {
7589a747e4fSDavid du Colombier 	char *s;
7599a747e4fSDavid du Colombier 	va_list arg;
7609a747e4fSDavid du Colombier 
7619a747e4fSDavid du Colombier 	va_start(arg, f);
7629a747e4fSDavid du Colombier 	s = vsmprint(f, arg);
7639a747e4fSDavid du Colombier 	va_end(arg);
7649a747e4fSDavid du Colombier 	fprint(2, "error: %s\n", s);
7659a747e4fSDavid du Colombier 	free(s);
7669a747e4fSDavid du Colombier 	errors = 1;
7679a747e4fSDavid du Colombier }
7689a747e4fSDavid du Colombier 
7699a747e4fSDavid du Colombier int
ismatch(char * s)7709a747e4fSDavid du Colombier ismatch(char *s)
7719a747e4fSDavid du Colombier {
7729a747e4fSDavid du Colombier 	int i, len;
7739a747e4fSDavid du Colombier 
7749a747e4fSDavid du Colombier 	if(nmatch == 0)
7759a747e4fSDavid du Colombier 		return 1;
7769a747e4fSDavid du Colombier 	for(i=0; i<nmatch; i++){
7779a747e4fSDavid du Colombier 		len = strlen(match[i]);
7780b9a5132SDavid du Colombier 		if(len == 0)
7790b9a5132SDavid du Colombier 			return 1;
7802cca75a1SDavid du Colombier 		if(strncmp(s, match[i], len) == 0 && (s[len]=='/' || s[len] == 0))
7819a747e4fSDavid du Colombier 			return 1;
7829a747e4fSDavid du Colombier 	}
7839a747e4fSDavid du Colombier 	return 0;
7849a747e4fSDavid du Colombier }
7859a747e4fSDavid du Colombier 
7869a747e4fSDavid du Colombier int
localdirstat(char * name,Dir * d)7879a747e4fSDavid du Colombier localdirstat(char *name, Dir *d)
7889a747e4fSDavid du Colombier {
7899a747e4fSDavid du Colombier 	static Dir *d2;
7909a747e4fSDavid du Colombier 
7919a747e4fSDavid du Colombier 	free(d2);
7929a747e4fSDavid du Colombier 	if((d2 = dirstat(name)) == nil)
7939a747e4fSDavid du Colombier 		return -1;
7949a747e4fSDavid du Colombier 	*d = *d2;
7959a747e4fSDavid du Colombier 	return 0;
7969a747e4fSDavid du Colombier }
7979a747e4fSDavid du Colombier 
7989a747e4fSDavid du Colombier enum { DEFB = 8192 };
7999a747e4fSDavid du Colombier 
8009a747e4fSDavid du Colombier static int
cmp1(int fd1,int fd2)8018f5875f3SDavid du Colombier cmp1(int fd1, int fd2)
8028f5875f3SDavid du Colombier {
8038f5875f3SDavid du Colombier 	char buf1[DEFB];
8048f5875f3SDavid du Colombier 	char buf2[DEFB];
8058f5875f3SDavid du Colombier 	int n1, n2;
8068f5875f3SDavid du Colombier 
8078f5875f3SDavid du Colombier 	for(;;){
8088f5875f3SDavid du Colombier 		n1 = readn(fd1, buf1, DEFB);
8098f5875f3SDavid du Colombier 		n2 = readn(fd2, buf2, DEFB);
8108f5875f3SDavid du Colombier 		if(n1 < 0 || n2 < 0)
8118f5875f3SDavid du Colombier 			return -1;
8128f5875f3SDavid du Colombier 		if(n1 != n2)
8138f5875f3SDavid du Colombier 			return 0;
8148f5875f3SDavid du Colombier 		if(n1 == 0)
8158f5875f3SDavid du Colombier 			return 1;
8168f5875f3SDavid du Colombier 		if(memcmp(buf1, buf2, n1) != 0)
8178f5875f3SDavid du Colombier 			return 0;
8188f5875f3SDavid du Colombier 	}
8198f5875f3SDavid du Colombier }
8208f5875f3SDavid du Colombier 
8218f5875f3SDavid du Colombier static int
copy1(int fdf,int fdt,char * from,char * to)8229a747e4fSDavid du Colombier copy1(int fdf, int fdt, char *from, char *to)
8239a747e4fSDavid du Colombier {
8248847fb04SDavid du Colombier 	int i, n, rv, pid[Nwork];
8258847fb04SDavid du Colombier 	Waitmsg *w;
8269a747e4fSDavid du Colombier 
8278847fb04SDavid du Colombier 	n = 0;
8288847fb04SDavid du Colombier 	off = 0;
8298847fb04SDavid du Colombier 	for(i=0; i<Nwork; i++){
8308847fb04SDavid du Colombier 		switch(pid[n] = rfork(RFPROC|RFMEM)){
8318847fb04SDavid du Colombier 		case 0:
8328847fb04SDavid du Colombier 			notify(failure);
8338847fb04SDavid du Colombier 			worker(fdf, fdt, from, to);
8348847fb04SDavid du Colombier 		case -1:
8358847fb04SDavid du Colombier 			break;
8368847fb04SDavid du Colombier 		default:
8378847fb04SDavid du Colombier 			n++;
8388847fb04SDavid du Colombier 			break;
8398847fb04SDavid du Colombier 		}
8408847fb04SDavid du Colombier 	}
8418847fb04SDavid du Colombier 	if(n == 0){
8428847fb04SDavid du Colombier 		fprint(2, "cp: rfork: %r\n");
8438847fb04SDavid du Colombier 		return -1;
8448847fb04SDavid du Colombier 	}
8458847fb04SDavid du Colombier 
8469a747e4fSDavid du Colombier 	rv = 0;
8478847fb04SDavid du Colombier 	while((w = wait()) != nil){
8488847fb04SDavid du Colombier 		if(w->msg[0]){
8499a747e4fSDavid du Colombier 			rv = -1;
8508847fb04SDavid du Colombier 			for(i=0; i<n; i++)
8518847fb04SDavid du Colombier 				if(pid[i] > 0)
8528847fb04SDavid du Colombier 					postnote(PNPROC, pid[i], "failure");
8539a747e4fSDavid du Colombier 		}
8548847fb04SDavid du Colombier 		free(w);
8559a747e4fSDavid du Colombier 	}
8569a747e4fSDavid du Colombier 	return rv;
8579a747e4fSDavid du Colombier }
8589a747e4fSDavid du Colombier 
8598847fb04SDavid du Colombier void
worker(int fdf,int fdt,char * from,char * to)8608847fb04SDavid du Colombier worker(int fdf, int fdt, char *from, char *to)
8618847fb04SDavid du Colombier {
8628847fb04SDavid du Colombier 	char buf[DEFB], *bp;
8638847fb04SDavid du Colombier 	long len, n;
8648847fb04SDavid du Colombier 	vlong o;
8658847fb04SDavid du Colombier 
8668847fb04SDavid du Colombier 	len = sizeof(buf);
8678847fb04SDavid du Colombier 	bp = buf;
8688847fb04SDavid du Colombier 	o = nextoff();
8698847fb04SDavid du Colombier 
8708847fb04SDavid du Colombier 	while(n = pread(fdf, bp, len, o)){
8718847fb04SDavid du Colombier 		if(n < 0){
8728847fb04SDavid du Colombier 			fprint(2, "reading %s: %r\n", from);
8738847fb04SDavid du Colombier 			_exits("bad");
8748847fb04SDavid du Colombier 		}
8758847fb04SDavid du Colombier 		if(pwrite(fdt, buf, n, o) != n){
8768847fb04SDavid du Colombier 			fprint(2, "writing %s: %r\n", to);
8778847fb04SDavid du Colombier 			_exits("bad");
8788847fb04SDavid du Colombier 		}
8798847fb04SDavid du Colombier 		bp += n;
8808847fb04SDavid du Colombier 		o += n;
8818847fb04SDavid du Colombier 		len -= n;
8828847fb04SDavid du Colombier 		if(len == 0){
8838847fb04SDavid du Colombier 			len = sizeof buf;
8848847fb04SDavid du Colombier 			bp = buf;
8858847fb04SDavid du Colombier 			o = nextoff();
8868847fb04SDavid du Colombier 		}
8878847fb04SDavid du Colombier 	}
8888847fb04SDavid du Colombier 	_exits(nil);
8898847fb04SDavid du Colombier }
8908847fb04SDavid du Colombier 
8918847fb04SDavid du Colombier vlong
nextoff(void)8928847fb04SDavid du Colombier nextoff(void)
8938847fb04SDavid du Colombier {
8948847fb04SDavid du Colombier 	vlong o;
8958847fb04SDavid du Colombier 
8968847fb04SDavid du Colombier 	qlock(&lk);
8978847fb04SDavid du Colombier 	o = off;
8988847fb04SDavid du Colombier 	off += DEFB;
8998847fb04SDavid du Colombier 	qunlock(&lk);
9008847fb04SDavid du Colombier 
9018847fb04SDavid du Colombier 	return o;
9028847fb04SDavid du Colombier }
9038847fb04SDavid du Colombier 
9048847fb04SDavid du Colombier void
failure(void *,char * note)9058847fb04SDavid du Colombier failure(void*, char *note)
9068847fb04SDavid du Colombier {
9078847fb04SDavid du Colombier 	if(strcmp(note, "failure") == 0)
9088847fb04SDavid du Colombier 		_exits(nil);
9098847fb04SDavid du Colombier 	noted(NDFLT);
9108847fb04SDavid du Colombier }
9118847fb04SDavid du Colombier 
9128847fb04SDavid du Colombier 
9139a747e4fSDavid du Colombier static int
opentemp(char * template)9149a747e4fSDavid du Colombier opentemp(char *template)
9159a747e4fSDavid du Colombier {
9169a747e4fSDavid du Colombier 	int fd, i;
9179a747e4fSDavid du Colombier 	char *p;
9189a747e4fSDavid du Colombier 
9199a747e4fSDavid du Colombier 	p = estrdup(template);
9209a747e4fSDavid du Colombier 	fd = -1;
9219a747e4fSDavid du Colombier 	for(i=0; i<10; i++){
9229a747e4fSDavid du Colombier 		mktemp(p);
9239a747e4fSDavid du Colombier 		if((fd=create(p, ORDWR|OEXCL|ORCLOSE, 0000)) >= 0)
9249a747e4fSDavid du Colombier 			break;
9259a747e4fSDavid du Colombier 		strcpy(p, template);
9269a747e4fSDavid du Colombier 	}
9279a747e4fSDavid du Colombier 	if(fd < 0)
9289a747e4fSDavid du Colombier 		return -1;
9299a747e4fSDavid du Colombier 	strcpy(template, p);
9309a747e4fSDavid du Colombier 	free(p);
9319a747e4fSDavid du Colombier 	return fd;
9329a747e4fSDavid du Colombier }
9339a747e4fSDavid du Colombier 
934*66d98691SDavid du Colombier static int
copytotemp(char * remote,int rfd,Dir * d0)935*66d98691SDavid du Colombier copytotemp(char *remote, int rfd, Dir *d0)
9369a747e4fSDavid du Colombier {
937*66d98691SDavid du Colombier 	int tfd;
938*66d98691SDavid du Colombier 	char tmp[32];
939*66d98691SDavid du Colombier 	Dir *d1;
940*66d98691SDavid du Colombier 
941*66d98691SDavid du Colombier 	strcpy(tmp, "/tmp/replicaXXXXXXXX");
942*66d98691SDavid du Colombier 	tfd = opentemp(tmp);
943*66d98691SDavid du Colombier 	if(tfd < 0)
944*66d98691SDavid du Colombier 		return -1;
945*66d98691SDavid du Colombier 	if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){
946*66d98691SDavid du Colombier 		close(tfd);
947*66d98691SDavid du Colombier 		return -1;
948*66d98691SDavid du Colombier 	}
949*66d98691SDavid du Colombier 
950*66d98691SDavid du Colombier 	if(d0->qid.path != d1->qid.path
951*66d98691SDavid du Colombier 	|| d0->qid.vers != d1->qid.vers
952*66d98691SDavid du Colombier 	|| d0->mtime != d1->mtime
953*66d98691SDavid du Colombier 	|| d0->length != d1->length){
954*66d98691SDavid du Colombier 		/* file changed underfoot; go around again */
955*66d98691SDavid du Colombier 		free(d1);
956*66d98691SDavid du Colombier 		close(tfd);
957*66d98691SDavid du Colombier 		return -2;
958*66d98691SDavid du Colombier 	}
959*66d98691SDavid du Colombier 	free(d1);
960*66d98691SDavid du Colombier 	return tfd;
961*66d98691SDavid du Colombier }
962*66d98691SDavid du Colombier 
963*66d98691SDavid du Colombier int
copyfile(char * local,char * remote,char * name,Dir * d,int dowstat,int * printerror)964*66d98691SDavid du Colombier copyfile(char *local, char *remote, char *name, Dir *d, int dowstat,
965*66d98691SDavid du Colombier 	int *printerror)
966*66d98691SDavid du Colombier {
967*66d98691SDavid du Colombier 	Dir *d0, *dl;
9689a747e4fSDavid du Colombier 	Dir nd;
9699a747e4fSDavid du Colombier 	int rfd, tfd, wfd, didcreate;
9706b6b9ac8SDavid du Colombier 	char tmp[32], *p, *safe;
9715fab9909SDavid du Colombier 	char err[ERRMAX];
9729a747e4fSDavid du Colombier 
973*66d98691SDavid du Colombier 	do {
9749a747e4fSDavid du Colombier 		*printerror = 0;
9759a747e4fSDavid du Colombier 		if((rfd = open(remote, OREAD)) < 0)
9769a747e4fSDavid du Colombier 			return -1;
9779a747e4fSDavid du Colombier 
9789a747e4fSDavid du Colombier 		d0 = dirfstat(rfd);
9799a747e4fSDavid du Colombier 		if(d0 == nil){
9809a747e4fSDavid du Colombier 			close(rfd);
9819a747e4fSDavid du Colombier 			return -1;
9829a747e4fSDavid du Colombier 		}
9839a747e4fSDavid du Colombier 		*printerror = 1;
9846b6b9ac8SDavid du Colombier 		if(!tempspool){
9859a747e4fSDavid du Colombier 			tfd = rfd;
9869a747e4fSDavid du Colombier 			goto DoCopy;
9879a747e4fSDavid du Colombier 		}
988*66d98691SDavid du Colombier 
989*66d98691SDavid du Colombier 		tfd = copytotemp(remote, rfd, d0);
990*66d98691SDavid du Colombier 		close(rfd);
9919a747e4fSDavid du Colombier 		if (tfd < 0) {
9929a747e4fSDavid du Colombier 			free(d0);
993*66d98691SDavid du Colombier 			if (tfd == -1)
9949a747e4fSDavid du Colombier 				return -1;
9959a747e4fSDavid du Colombier 		}
996*66d98691SDavid du Colombier 	} while(tfd == -2);
9979a747e4fSDavid du Colombier 	if(seek(tfd, 0, 0) != 0){
9989a747e4fSDavid du Colombier 		close(tfd);
9999a747e4fSDavid du Colombier 		free(d0);
10009a747e4fSDavid du Colombier 		return -1;
10019a747e4fSDavid du Colombier 	}
10026b6b9ac8SDavid du Colombier 
10039a747e4fSDavid du Colombier DoCopy:
10046b6b9ac8SDavid du Colombier 	/*
10056b6b9ac8SDavid du Colombier 	 * clumsy but important hack to do safeinstall-like installs.
10066b6b9ac8SDavid du Colombier 	 */
10076b6b9ac8SDavid du Colombier 	p = strchr(name, '/');
10086b6b9ac8SDavid du Colombier 	if(safeinstall && p && strncmp(p, "/bin/", 5) == 0 && access(local, AEXIST) >= 0){
10096b6b9ac8SDavid du Colombier 		/*
10106b6b9ac8SDavid du Colombier 		 * remove bin/_targ
10116b6b9ac8SDavid du Colombier 		 */
10126b6b9ac8SDavid du Colombier 		safe = emalloc(strlen(local)+2);
10136b6b9ac8SDavid du Colombier 		strcpy(safe, local);
10146b6b9ac8SDavid du Colombier 		p = strrchr(safe, '/')+1;
10156b6b9ac8SDavid du Colombier 		memmove(p+1, p, strlen(p)+1);
10166b6b9ac8SDavid du Colombier 		p[0] = '_';
10176b6b9ac8SDavid du Colombier 		remove(safe);	/* ignore failure */
10186b6b9ac8SDavid du Colombier 
10196b6b9ac8SDavid du Colombier 		/*
10206b6b9ac8SDavid du Colombier 		 * rename bin/targ to bin/_targ
10216b6b9ac8SDavid du Colombier 		 */
10226b6b9ac8SDavid du Colombier 		nulldir(&nd);
10236b6b9ac8SDavid du Colombier 		nd.name = p;
10246b6b9ac8SDavid du Colombier 		if(dirwstat(local, &nd) < 0)
10256b6b9ac8SDavid du Colombier 			fprint(2, "warning: rename %s to %s: %r\n", local, p);
10266b6b9ac8SDavid du Colombier 	}
10276b6b9ac8SDavid du Colombier 
10289a747e4fSDavid du Colombier 	didcreate = 0;
10295fab9909SDavid du Colombier 	if((dl = dirstat(local)) == nil){
10305fab9909SDavid du Colombier 		if((wfd = create(local, OWRITE, 0)) >= 0){
10315fab9909SDavid du Colombier 			didcreate = 1;
10325fab9909SDavid du Colombier 			goto okay;
10335fab9909SDavid du Colombier 		}
10345fab9909SDavid du Colombier 		goto err;
10355fab9909SDavid du Colombier 	}else{
10365fab9909SDavid du Colombier 		if((wfd = open(local, OTRUNC|OWRITE)) >= 0)
10375fab9909SDavid du Colombier 			goto okay;
10385fab9909SDavid du Colombier 		rerrstr(err, sizeof err);
10395fab9909SDavid du Colombier 		if(strstr(err, "permission") == nil)
10405fab9909SDavid du Colombier 			goto err;
10415fab9909SDavid du Colombier 		nulldir(&nd);
10425fab9909SDavid du Colombier 		/*
10435fab9909SDavid du Colombier 		 * Assume the person running pull is in the appropriate
10445fab9909SDavid du Colombier 		 * groups.  We could set 0666 instead, but I'm worried
10455fab9909SDavid du Colombier 		 * about leaving the file world-readable or world-writable
10465fab9909SDavid du Colombier 		 * when it shouldn't be.
10475fab9909SDavid du Colombier 		 */
10485fab9909SDavid du Colombier 		nd.mode = dl->mode | 0660;
10495fab9909SDavid du Colombier 		if(nd.mode == dl->mode)
10505fab9909SDavid du Colombier 			goto err;
10515fab9909SDavid du Colombier 		if(dirwstat(local, &nd) < 0)
10525fab9909SDavid du Colombier 			goto err;
10535fab9909SDavid du Colombier 		if((wfd = open(local, OTRUNC|OWRITE)) >= 0){
10545fab9909SDavid du Colombier 			nd.mode = dl->mode;
10555fab9909SDavid du Colombier 			if(dirfwstat(wfd, &nd) < 0)
10565fab9909SDavid du Colombier 				fprint(2, "warning: set mode on %s to 0660 to open; cannot set back to %luo: %r\n", local, nd.mode);
10575fab9909SDavid du Colombier 			goto okay;
10585fab9909SDavid du Colombier 		}
10595fab9909SDavid du Colombier 		nd.mode = dl->mode;
10605fab9909SDavid du Colombier 		if(dirwstat(local, &nd) < 0)
10615fab9909SDavid du Colombier 			fprint(2, "warning: set mode on %s to %luo to open; open failed; cannot set mode back to %luo: %r\n", local, nd.mode|0660, nd.mode);
10625fab9909SDavid du Colombier 		goto err;
10635fab9909SDavid du Colombier 	}
10645fab9909SDavid du Colombier 
10655fab9909SDavid du Colombier err:
10669a747e4fSDavid du Colombier 	close(tfd);
10679a747e4fSDavid du Colombier 	free(d0);
10685fab9909SDavid du Colombier 	free(dl);
10699a747e4fSDavid du Colombier 	return -1;
10705fab9909SDavid du Colombier 
10715fab9909SDavid du Colombier okay:
10725fab9909SDavid du Colombier 	free(dl);
10739a747e4fSDavid du Colombier 	if(copy1(tfd, wfd, tmp, local) < 0){
10749a747e4fSDavid du Colombier 		close(tfd);
10759a747e4fSDavid du Colombier 		close(wfd);
10769a747e4fSDavid du Colombier 		free(d0);
10779a747e4fSDavid du Colombier 		return -1;
10789a747e4fSDavid du Colombier 	}
10799a747e4fSDavid du Colombier 	close(tfd);
10809a747e4fSDavid du Colombier 	if(didcreate || dowstat){
10819a747e4fSDavid du Colombier 		nulldir(&nd);
10829a747e4fSDavid du Colombier 		nd.mode = d->mode;
10839a747e4fSDavid du Colombier 		if(dirfwstat(wfd, &nd) < 0)
10849a747e4fSDavid du Colombier 			fprint(2, "warning: cannot set mode on %s\n", local);
10859a747e4fSDavid du Colombier 		nulldir(&nd);
10869a747e4fSDavid du Colombier 		nd.gid = d->gid;
10879a747e4fSDavid du Colombier 		if(dirfwstat(wfd, &nd) < 0)
10889a747e4fSDavid du Colombier 			fprint(2, "warning: cannot set gid on %s\n", local);
10899a747e4fSDavid du Colombier 		if(douid){
10909a747e4fSDavid du Colombier 			nulldir(&nd);
10919a747e4fSDavid du Colombier 			nd.uid = d->uid;
10929a747e4fSDavid du Colombier 			if(dirfwstat(wfd, &nd) < 0)
10939a747e4fSDavid du Colombier 				fprint(2, "warning: cannot set uid on %s\n", local);
10949a747e4fSDavid du Colombier 		}
10959a747e4fSDavid du Colombier 	}
10969a747e4fSDavid du Colombier 	d->mtime = d0->mtime;
10979a747e4fSDavid du Colombier 	d->length = d0->length;
10989a747e4fSDavid du Colombier 	nulldir(&nd);
10999a747e4fSDavid du Colombier 	nd.mtime = d->mtime;
11009a747e4fSDavid du Colombier 	if(dirfwstat(wfd, &nd) < 0)
11019a747e4fSDavid du Colombier 		fprint(2, "warning: cannot set mtime on %s\n", local);
11029a747e4fSDavid du Colombier 	free(d0);
11036b6b9ac8SDavid du Colombier 
11046b6b9ac8SDavid du Colombier 	close(wfd);
11059a747e4fSDavid du Colombier 	return 0;
11069a747e4fSDavid du Colombier }
11079a747e4fSDavid du Colombier 
11088f5875f3SDavid du Colombier int
samecontents(char * local,char * remote)11098f5875f3SDavid du Colombier samecontents(char *local, char *remote)
11108f5875f3SDavid du Colombier {
11118f5875f3SDavid du Colombier 	Dir *d0, *d1;
11128f5875f3SDavid du Colombier 	int rfd, tfd, lfd, ret;
11138f5875f3SDavid du Colombier 
11148f5875f3SDavid du Colombier 	/* quick check: sizes must match */
11158f5875f3SDavid du Colombier 	d1 = nil;
11168f5875f3SDavid du Colombier 	if((d0 = dirstat(local)) == nil || (d1 = dirstat(remote)) == nil){
11178f5875f3SDavid du Colombier 		free(d0);
11188f5875f3SDavid du Colombier 		free(d1);
11198f5875f3SDavid du Colombier 		return -1;
11208f5875f3SDavid du Colombier 	}
11218f5875f3SDavid du Colombier 	if(d0->length != d1->length){
11228f5875f3SDavid du Colombier 		free(d0);
11238f5875f3SDavid du Colombier 		free(d1);
11248f5875f3SDavid du Colombier 		return 0;
11258f5875f3SDavid du Colombier 	}
11268f5875f3SDavid du Colombier 
1127*66d98691SDavid du Colombier 	do {
11288f5875f3SDavid du Colombier 		if((rfd = open(remote, OREAD)) < 0)
11298f5875f3SDavid du Colombier 			return -1;
11308f5875f3SDavid du Colombier 		d0 = dirfstat(rfd);
11318f5875f3SDavid du Colombier 		if(d0 == nil){
11328f5875f3SDavid du Colombier 			close(rfd);
11338f5875f3SDavid du Colombier 			return -1;
11348f5875f3SDavid du Colombier 		}
11358f5875f3SDavid du Colombier 
1136*66d98691SDavid du Colombier 		tfd = copytotemp(remote, rfd, d0);
11378f5875f3SDavid du Colombier 		close(rfd);
11388f5875f3SDavid du Colombier 		free(d0);
1139*66d98691SDavid du Colombier 		if (tfd == -1)
11408f5875f3SDavid du Colombier 			return -1;
1141*66d98691SDavid du Colombier 	} while(tfd == -2);
11428f5875f3SDavid du Colombier 	if(seek(tfd, 0, 0) != 0){
11438f5875f3SDavid du Colombier 		close(tfd);
11448f5875f3SDavid du Colombier 		return -1;
11458f5875f3SDavid du Colombier 	}
11468f5875f3SDavid du Colombier 
11478f5875f3SDavid du Colombier 	/*
11488f5875f3SDavid du Colombier 	 * now compare
11498f5875f3SDavid du Colombier 	 */
11508f5875f3SDavid du Colombier 	if((lfd = open(local, OREAD)) < 0){
11518f5875f3SDavid du Colombier 		close(tfd);
11528f5875f3SDavid du Colombier 		return -1;
11538f5875f3SDavid du Colombier 	}
11548f5875f3SDavid du Colombier 
11558f5875f3SDavid du Colombier 	ret = cmp1(lfd, tfd);
11568f5875f3SDavid du Colombier 	close(lfd);
11578f5875f3SDavid du Colombier 	close(tfd);
11588f5875f3SDavid du Colombier 	return ret;
11598f5875f3SDavid du Colombier }
11608f5875f3SDavid du Colombier 
11619a747e4fSDavid du Colombier /*
11629a747e4fSDavid du Colombier  * Applylog might try to overwrite itself.
11639a747e4fSDavid du Colombier  * To avoid problems with this, we copy ourselves
11649a747e4fSDavid du Colombier  * into /tmp and then re-exec.
11659a747e4fSDavid du Colombier  */
11669a747e4fSDavid du Colombier char *rmargv0;
11679a747e4fSDavid du Colombier 
11689a747e4fSDavid du Colombier static void
rmself(void)11699a747e4fSDavid du Colombier rmself(void)
11709a747e4fSDavid du Colombier {
11719a747e4fSDavid du Colombier 	remove(rmargv0);
11729a747e4fSDavid du Colombier }
11739a747e4fSDavid du Colombier 
11749a747e4fSDavid du Colombier static int
genopentemp(char * template,int mode,int perm)11759a747e4fSDavid du Colombier genopentemp(char *template, int mode, int perm)
11769a747e4fSDavid du Colombier {
11779a747e4fSDavid du Colombier 	int fd, i;
11789a747e4fSDavid du Colombier 	char *p;
11799a747e4fSDavid du Colombier 
11809a747e4fSDavid du Colombier 	p = estrdup(template);
11819a747e4fSDavid du Colombier 	fd = -1;
11829a747e4fSDavid du Colombier 	for(i=0; i<10; i++){
11839a747e4fSDavid du Colombier 		mktemp(p);
11849a747e4fSDavid du Colombier 		if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
11859a747e4fSDavid du Colombier 			break;
11869a747e4fSDavid du Colombier 		strcpy(p, template);
11879a747e4fSDavid du Colombier 	}
11889a747e4fSDavid du Colombier 	if(fd < 0)
11899a747e4fSDavid du Colombier 		sysfatal("could not create temporary file");
11909a747e4fSDavid du Colombier 
11919a747e4fSDavid du Colombier 	strcpy(template, p);
11929a747e4fSDavid du Colombier 	free(p);
11939a747e4fSDavid du Colombier 
11949a747e4fSDavid du Colombier 	return fd;
11959a747e4fSDavid du Colombier }
11969a747e4fSDavid du Colombier 
11979a747e4fSDavid du Colombier static void
membogus(char ** argv)11989a747e4fSDavid du Colombier membogus(char **argv)
11999a747e4fSDavid du Colombier {
12009a747e4fSDavid du Colombier 	int n, fd, wfd;
12019a747e4fSDavid du Colombier 	char template[50], buf[1024];
12029a747e4fSDavid du Colombier 
12035fab9909SDavid du Colombier 	if(strncmp(argv[0], "/tmp/_applylog_", 1+3+1+1+8+1)==0) {
12049a747e4fSDavid du Colombier 		rmargv0 = argv[0];
12059a747e4fSDavid du Colombier 		atexit(rmself);
12069a747e4fSDavid du Colombier 		return;
12079a747e4fSDavid du Colombier 	}
12089a747e4fSDavid du Colombier 
12099a747e4fSDavid du Colombier 	if((fd = open(argv[0], OREAD)) < 0)
12109a747e4fSDavid du Colombier 		return;
12119a747e4fSDavid du Colombier 
12129a747e4fSDavid du Colombier 	strcpy(template, "/tmp/_applylog_XXXXXX");
12139a747e4fSDavid du Colombier 	if((wfd = genopentemp(template, OWRITE, 0700)) < 0)
12149a747e4fSDavid du Colombier 		return;
12159a747e4fSDavid du Colombier 
12169a747e4fSDavid du Colombier 	while((n = read(fd, buf, sizeof buf)) > 0)
12179a747e4fSDavid du Colombier 		if(write(wfd, buf, n) != n)
12189a747e4fSDavid du Colombier 			goto Error;
12199a747e4fSDavid du Colombier 
12209a747e4fSDavid du Colombier 	if(n != 0)
12219a747e4fSDavid du Colombier 		goto Error;
12229a747e4fSDavid du Colombier 
12239a747e4fSDavid du Colombier 	close(fd);
12249a747e4fSDavid du Colombier 	close(wfd);
12259a747e4fSDavid du Colombier 
12269a747e4fSDavid du Colombier 	argv[0] = template;
12279a747e4fSDavid du Colombier 	exec(template, argv);
12289a747e4fSDavid du Colombier 	fprint(2, "exec error %r\n");
12299a747e4fSDavid du Colombier 
12309a747e4fSDavid du Colombier Error:
12319a747e4fSDavid du Colombier 	close(fd);
12329a747e4fSDavid du Colombier 	close(wfd);
12339a747e4fSDavid du Colombier 	remove(template);
12349a747e4fSDavid du Colombier 	return;
12359a747e4fSDavid du Colombier }
1236