xref: /plan9/sys/src/cmd/replica/applychanges.c (revision 1066d6debf4f3ce80fbab98c906650d920c13a7a)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * push changes from client to server.
39a747e4fSDavid du Colombier  */
49a747e4fSDavid du Colombier #include "all.h"
59a747e4fSDavid du Colombier 
69a747e4fSDavid du Colombier int douid;
79a747e4fSDavid du Colombier Db *db;
89a747e4fSDavid du Colombier char **x;
99a747e4fSDavid du Colombier int nx;
109a747e4fSDavid du Colombier int justshow;
119a747e4fSDavid du Colombier int verbose;
129a747e4fSDavid du Colombier int conflicts;
139a747e4fSDavid du Colombier char newpath[10000];
149a747e4fSDavid du Colombier char oldpath[10000];
159a747e4fSDavid du Colombier char *clientroot;
169a747e4fSDavid du Colombier char *serverroot;
179a747e4fSDavid du Colombier int copyfile(char*, char*, Dir*, int);
189a747e4fSDavid du Colombier int metafile(char*, Dir*);
199a747e4fSDavid du Colombier char **match;
209a747e4fSDavid du Colombier int nmatch;
219a747e4fSDavid du Colombier 
229a747e4fSDavid du Colombier int
ismatch(char * s)239a747e4fSDavid du Colombier ismatch(char *s)
249a747e4fSDavid du Colombier {
259a747e4fSDavid du Colombier 	int i, len;
269a747e4fSDavid du Colombier 
279a747e4fSDavid du Colombier 	if(nmatch == 0)
289a747e4fSDavid du Colombier 		return 1;
299a747e4fSDavid du Colombier 	for(i=0; i<nmatch; i++){
309a747e4fSDavid du Colombier 		if(strcmp(s, match[i]) == 0)
319a747e4fSDavid du Colombier 			return 1;
329a747e4fSDavid du Colombier 		len = strlen(match[i]);
339a747e4fSDavid du Colombier 		if(strncmp(s, match[i], len) == 0 && s[len]=='/')
349a747e4fSDavid du Colombier 			return 1;
359a747e4fSDavid du Colombier 	}
369a747e4fSDavid du Colombier 	return 0;
379a747e4fSDavid du Colombier }
389a747e4fSDavid du Colombier 
399a747e4fSDavid du Colombier void
xlog(char c,char * path,Dir * d)409a747e4fSDavid du Colombier xlog(char c, char *path, Dir *d)
419a747e4fSDavid du Colombier {
429a747e4fSDavid du Colombier 	if(!verbose)
439a747e4fSDavid du Colombier 		return;
449a747e4fSDavid du Colombier 	print("%c %s %luo %s %s %lud\n", c, path, d->mode, d->uid, d->gid, d->mtime);
459a747e4fSDavid du Colombier }
469a747e4fSDavid du Colombier 
479a747e4fSDavid du Colombier void
walk(char * new,char * old,Dir * pd,void *)489a747e4fSDavid du Colombier walk(char *new, char *old, Dir *pd, void*)
499a747e4fSDavid du Colombier {
509a747e4fSDavid du Colombier 	int i, len;
519a747e4fSDavid du Colombier 	Dir od, d;
529a747e4fSDavid du Colombier 	static Dir *xd;
539a747e4fSDavid du Colombier 
543ff48bf5SDavid du Colombier 	new = unroot(new, "/");
553ff48bf5SDavid du Colombier 	old = unroot(old, serverroot);
563ff48bf5SDavid du Colombier 
579a747e4fSDavid du Colombier 	if(!ismatch(new))
589a747e4fSDavid du Colombier 		return;
599a747e4fSDavid du Colombier 	if(xd != nil){
609a747e4fSDavid du Colombier 		free(xd);
619a747e4fSDavid du Colombier 		xd = nil;
629a747e4fSDavid du Colombier 	}
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	for(i=0; i<nx; i++){
659a747e4fSDavid du Colombier 		if(strcmp(new, x[i]) == 0)
669a747e4fSDavid du Colombier 			return;
679a747e4fSDavid du Colombier 		len = strlen(x[i]);
689a747e4fSDavid du Colombier 		if(strncmp(new, x[i], len)==0 && new[len]=='/')
699a747e4fSDavid du Colombier 			return;
709a747e4fSDavid du Colombier 	}
719a747e4fSDavid du Colombier 
729a747e4fSDavid du Colombier 	d = *pd;
739a747e4fSDavid du Colombier 	d.name = old;
749a747e4fSDavid du Colombier 	memset(&od, 0, sizeof od);
759a747e4fSDavid du Colombier 	snprint(newpath, sizeof newpath, "%s/%s", clientroot, new);
769a747e4fSDavid du Colombier 	snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, old);
779a747e4fSDavid du Colombier 
789a747e4fSDavid du Colombier 	xd = dirstat(oldpath);
799a747e4fSDavid du Colombier 	if(markdb(db, new, &od) < 0){
809a747e4fSDavid du Colombier 		if(xd != nil){
819a747e4fSDavid du Colombier 			print("x %s create/create conflict\n", new);
829a747e4fSDavid du Colombier 			conflicts = 1;
839a747e4fSDavid du Colombier 			return;
849a747e4fSDavid du Colombier 		}
859a747e4fSDavid du Colombier 		xlog('a', new, &d);
869a747e4fSDavid du Colombier 		d.muid = "mark";	/* mark bit */
879a747e4fSDavid du Colombier 		if(!justshow){
889a747e4fSDavid du Colombier 			if(copyfile(newpath, oldpath, &d, 1) == 0)
899a747e4fSDavid du Colombier 				insertdb(db, new, &d);
909a747e4fSDavid du Colombier 		}
919a747e4fSDavid du Colombier 	}else{
929a747e4fSDavid du Colombier 		if((d.mode&DMDIR)==0 && od.mtime!=d.mtime){
939a747e4fSDavid du Colombier 			if(xd==nil){
949a747e4fSDavid du Colombier 				print("%s update/remove conflict\n", new);
959a747e4fSDavid du Colombier 				conflicts = 1;
969a747e4fSDavid du Colombier 				return;
979a747e4fSDavid du Colombier 			}
989a747e4fSDavid du Colombier 			if(xd->mtime != od.mtime){
999a747e4fSDavid du Colombier 				print("%s update/update conflict\n", new);
1009a747e4fSDavid du Colombier 				conflicts = 1;
1019a747e4fSDavid du Colombier 				return;
1029a747e4fSDavid du Colombier 			}
1039a747e4fSDavid du Colombier 			od.mtime = d.mtime;
104220e960cSDavid du Colombier 			od.muid = "mark";
1059a747e4fSDavid du Colombier 			xlog('c', new, &od);
1069a747e4fSDavid du Colombier 			if(!justshow){
107220e960cSDavid du Colombier 				if(copyfile(newpath, oldpath, &od, 0) == 0)
1089a747e4fSDavid du Colombier 					insertdb(db, new, &od);
1099a747e4fSDavid du Colombier 			}
1109a747e4fSDavid du Colombier 		}
1119a747e4fSDavid du Colombier 		if((douid&&strcmp(od.uid,d.uid)!=0)
1129a747e4fSDavid du Colombier 		|| strcmp(od.gid,d.gid)!=0
1139a747e4fSDavid du Colombier 		|| od.mode!=d.mode){
1149a747e4fSDavid du Colombier 			if(xd==nil){
1159a747e4fSDavid du Colombier 				print("%s metaupdate/remove conflict\n", new);
1169a747e4fSDavid du Colombier 				conflicts = 1;
1179a747e4fSDavid du Colombier 				return;
1189a747e4fSDavid du Colombier 			}
1199a747e4fSDavid du Colombier 			if((douid&&strcmp(od.uid,xd->uid)!=0)
1209a747e4fSDavid du Colombier 			|| strcmp(od.uid,xd->gid)!=0
1219a747e4fSDavid du Colombier 			|| od.mode!=xd->mode){
1229a747e4fSDavid du Colombier 				print("%s metaupdate/metaupdate conflict\n", new);
1239a747e4fSDavid du Colombier 				conflicts = 1;
1249a747e4fSDavid du Colombier 				return;
1259a747e4fSDavid du Colombier 			}
1269a747e4fSDavid du Colombier 			if(douid)
1279a747e4fSDavid du Colombier 				od.uid = d.uid;
1289a747e4fSDavid du Colombier 			od.gid = d.gid;
1299a747e4fSDavid du Colombier 			od.mode = d.mode;
130220e960cSDavid du Colombier 			od.muid = "mark";
1319a747e4fSDavid du Colombier 			xlog('m', new, &od);
1329a747e4fSDavid du Colombier 			if(!justshow){
1339a747e4fSDavid du Colombier 				if(metafile(oldpath, &od) == 0)
1349a747e4fSDavid du Colombier 					insertdb(db, new, &od);
1359a747e4fSDavid du Colombier 			}
1369a747e4fSDavid du Colombier 		}
1379a747e4fSDavid du Colombier 	}
1389a747e4fSDavid du Colombier }
1399a747e4fSDavid du Colombier 
1409a747e4fSDavid du Colombier void
usage(void)1419a747e4fSDavid du Colombier usage(void)
1429a747e4fSDavid du Colombier {
143*1066d6deSDavid du Colombier 	fprint(2, "usage: replica/applychanges [-nuv] [-p proto] [-x path]... clientdb clientroot serverroot [path ...]\n");
1449a747e4fSDavid du Colombier 	exits("usage");
1459a747e4fSDavid du Colombier }
1469a747e4fSDavid du Colombier 
1479a747e4fSDavid du Colombier void
main(int argc,char ** argv)1489a747e4fSDavid du Colombier main(int argc, char **argv)
1499a747e4fSDavid du Colombier {
1509a747e4fSDavid du Colombier 	char *proto;
1519a747e4fSDavid du Colombier 	Avlwalk *w;
1529a747e4fSDavid du Colombier 	Dir *xd, d;
1539a747e4fSDavid du Colombier 	Entry *e;
1549a747e4fSDavid du Colombier 
1559a747e4fSDavid du Colombier 	quotefmtinstall();
1569a747e4fSDavid du Colombier 	proto = "/sys/lib/sysconfig/proto/allproto";
1579a747e4fSDavid du Colombier 	ARGBEGIN{
1589a747e4fSDavid du Colombier 	case 'n':
1599a747e4fSDavid du Colombier 		justshow = 1;
1609a747e4fSDavid du Colombier 		verbose = 1;
1619a747e4fSDavid du Colombier 		break;
1629a747e4fSDavid du Colombier 	case 'p':
1639a747e4fSDavid du Colombier 		proto = EARGF(usage());
1649a747e4fSDavid du Colombier 		break;
1659a747e4fSDavid du Colombier 	case 'u':
1669a747e4fSDavid du Colombier 		douid = 1;
1679a747e4fSDavid du Colombier 		break;
1689a747e4fSDavid du Colombier 	case 'v':
1699a747e4fSDavid du Colombier 		verbose = 1;
1709a747e4fSDavid du Colombier 		break;
1719a747e4fSDavid du Colombier 	case 'x':
1729a747e4fSDavid du Colombier 		if(nx%16 == 0)
1739a747e4fSDavid du Colombier 			x = erealloc(x, (nx+16)*sizeof(x[0]));
1749a747e4fSDavid du Colombier 		x[nx++] = EARGF(usage());
1759a747e4fSDavid du Colombier 		break;
1769a747e4fSDavid du Colombier 	default:
1779a747e4fSDavid du Colombier 		usage();
1789a747e4fSDavid du Colombier 	}ARGEND
1799a747e4fSDavid du Colombier 
1809a747e4fSDavid du Colombier 	if(argc < 3)
1819a747e4fSDavid du Colombier 		usage();
1829a747e4fSDavid du Colombier 
1839a747e4fSDavid du Colombier 	db = opendb(argv[0]);
1849a747e4fSDavid du Colombier 	clientroot = argv[1];
1859a747e4fSDavid du Colombier 	serverroot = argv[2];
1869a747e4fSDavid du Colombier 	match = argv+3;
1879a747e4fSDavid du Colombier 	nmatch = argc-3;
1889a747e4fSDavid du Colombier 
1899a747e4fSDavid du Colombier 
1909a747e4fSDavid du Colombier 	if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0)
1919a747e4fSDavid du Colombier 		sysfatal("rdproto: %r");
1929a747e4fSDavid du Colombier 
1939a747e4fSDavid du Colombier 	w = avlwalk(db->avl);
194867bfcc6SDavid du Colombier 	while(e = (Entry*)avlprev(w)){
1959a747e4fSDavid du Colombier 		if(!ismatch(e->name))
1969a747e4fSDavid du Colombier 			continue;
1979a747e4fSDavid du Colombier 		if(!e->d.mark){		/* not visited during walk */
1989a747e4fSDavid du Colombier 			snprint(newpath, sizeof newpath, "%s/%s", clientroot, e->name);
1999a747e4fSDavid du Colombier 			snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, e->d.name);
2009a747e4fSDavid du Colombier 			xd = dirstat(oldpath);
2019a747e4fSDavid du Colombier 			if(xd == nil){
2029a747e4fSDavid du Colombier 				removedb(db, e->name);
2039a747e4fSDavid du Colombier 				continue;
2049a747e4fSDavid du Colombier 			}
205220e960cSDavid du Colombier 			if(xd->mtime != e->d.mtime && (e->d.mode&xd->mode&DMDIR)==0){
2069a747e4fSDavid du Colombier 				print("x %q remove/update conflict\n", e->name);
2079a747e4fSDavid du Colombier 				free(xd);
2089a747e4fSDavid du Colombier 				continue;
2099a747e4fSDavid du Colombier 			}
2109a747e4fSDavid du Colombier 			memset(&d, 0, sizeof d);
2119a747e4fSDavid du Colombier 			d.name = e->d.name;
2129a747e4fSDavid du Colombier 			d.uid = e->d.uid;
2139a747e4fSDavid du Colombier 			d.gid = e->d.gid;
2149a747e4fSDavid du Colombier 			d.mtime = e->d.mtime;
2159a747e4fSDavid du Colombier 			d.mode = e->d.mode;
2169a747e4fSDavid du Colombier 			xlog('d', e->name, &d);
2179a747e4fSDavid du Colombier 			if(!justshow){
2189a747e4fSDavid du Colombier 				if(remove(oldpath) == 0)
2199a747e4fSDavid du Colombier 					removedb(db, e->name);
2209a747e4fSDavid du Colombier 			}
221220e960cSDavid du Colombier 			free(xd);
2229a747e4fSDavid du Colombier 		}
2239a747e4fSDavid du Colombier 	}
2249a747e4fSDavid du Colombier 
2259a747e4fSDavid du Colombier 	if(conflicts)
2269a747e4fSDavid du Colombier 		exits("conflicts");
2279a747e4fSDavid du Colombier 	exits(nil);
2289a747e4fSDavid du Colombier }
2299a747e4fSDavid du Colombier 
2309a747e4fSDavid du Colombier enum { DEFB = 8192 };
2319a747e4fSDavid du Colombier 
2329a747e4fSDavid du Colombier static int
copy1(int fdf,int fdt,char * from,char * to)2339a747e4fSDavid du Colombier copy1(int fdf, int fdt, char *from, char *to)
2349a747e4fSDavid du Colombier {
2359a747e4fSDavid du Colombier 	char buf[DEFB];
2369a747e4fSDavid du Colombier 	long n, n1, rcount;
2379a747e4fSDavid du Colombier 	int rv;
2389a747e4fSDavid du Colombier 	char err[ERRMAX];
2399a747e4fSDavid du Colombier 
2409a747e4fSDavid du Colombier 	/* clear any residual error */
2419a747e4fSDavid du Colombier 	err[0] = '\0';
2429a747e4fSDavid du Colombier 	errstr(err, ERRMAX);
2439a747e4fSDavid du Colombier 	rv = 0;
2449a747e4fSDavid du Colombier 	for(rcount=0;; rcount++) {
2459a747e4fSDavid du Colombier 		n = read(fdf, buf, DEFB);
2469a747e4fSDavid du Colombier 		if(n <= 0)
2479a747e4fSDavid du Colombier 			break;
2489a747e4fSDavid du Colombier 		n1 = write(fdt, buf, n);
2499a747e4fSDavid du Colombier 		if(n1 != n) {
2509a747e4fSDavid du Colombier 			fprint(2, "error writing %q: %r\n", to);
2519a747e4fSDavid du Colombier 			rv = -1;
2529a747e4fSDavid du Colombier 			break;
2539a747e4fSDavid du Colombier 		}
2549a747e4fSDavid du Colombier 	}
2559a747e4fSDavid du Colombier 	if(n < 0) {
2569a747e4fSDavid du Colombier 		fprint(2, "error reading %q: %r\n", from);
2579a747e4fSDavid du Colombier 		rv = -1;
2589a747e4fSDavid du Colombier 	}
2599a747e4fSDavid du Colombier 	return rv;
2609a747e4fSDavid du Colombier }
2619a747e4fSDavid du Colombier 
2629a747e4fSDavid du Colombier int
copyfile(char * from,char * to,Dir * d,int dowstat)2639a747e4fSDavid du Colombier copyfile(char *from, char *to, Dir *d, int dowstat)
2649a747e4fSDavid du Colombier {
2659a747e4fSDavid du Colombier 	Dir nd;
2669a747e4fSDavid du Colombier 	int rfd, wfd, didcreate;
2679a747e4fSDavid du Colombier 
2689a747e4fSDavid du Colombier 	if((rfd = open(from, OREAD)) < 0)
2699a747e4fSDavid du Colombier 		return -1;
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier 	didcreate = 0;
272220e960cSDavid du Colombier 	if(d->mode&DMDIR){
273220e960cSDavid du Colombier 		if((wfd = create(to, OREAD, DMDIR)) < 0){
274220e960cSDavid du Colombier 			fprint(2, "mkdir %q: %r\n", to);
275220e960cSDavid du Colombier 			close(rfd);
276220e960cSDavid du Colombier 			return -1;
277220e960cSDavid du Colombier 		}
278220e960cSDavid du Colombier 	}else{
2799a747e4fSDavid du Colombier 		if((wfd = open(to, OTRUNC|OWRITE)) < 0){
2809a747e4fSDavid du Colombier 			if((wfd = create(to, OWRITE, 0)) < 0){
2819a747e4fSDavid du Colombier 				close(rfd);
2829a747e4fSDavid du Colombier 				return -1;
2839a747e4fSDavid du Colombier 			}
2849a747e4fSDavid du Colombier 			didcreate = 1;
2859a747e4fSDavid du Colombier 		}
2869a747e4fSDavid du Colombier 		if(copy1(rfd, wfd, from, to) < 0){
2879a747e4fSDavid du Colombier 			close(rfd);
2889a747e4fSDavid du Colombier 			close(wfd);
2899a747e4fSDavid du Colombier 			return -1;
2909a747e4fSDavid du Colombier 		}
291220e960cSDavid du Colombier 	}
292220e960cSDavid du Colombier 	close(rfd);
2939a747e4fSDavid du Colombier 	if(didcreate || dowstat){
2949a747e4fSDavid du Colombier 		nulldir(&nd);
2959a747e4fSDavid du Colombier 		nd.mode = d->mode;
2969a747e4fSDavid du Colombier 		if(dirfwstat(wfd, &nd) < 0)
2979a747e4fSDavid du Colombier 			fprint(2, "warning: cannot set mode on %q\n", to);
2989a747e4fSDavid du Colombier 		nulldir(&nd);
2999a747e4fSDavid du Colombier 		nd.gid = d->gid;
3009a747e4fSDavid du Colombier 		if(dirfwstat(wfd, &nd) < 0)
3019a747e4fSDavid du Colombier 			fprint(2, "warning: cannot set gid on %q\n", to);
3029a747e4fSDavid du Colombier 		if(douid){
3039a747e4fSDavid du Colombier 			nulldir(&nd);
3049a747e4fSDavid du Colombier 			nd.uid = d->uid;
3059a747e4fSDavid du Colombier 			if(dirfwstat(wfd, &nd) < 0)
3069a747e4fSDavid du Colombier 				fprint(2, "warning: cannot set uid on %q\n", to);
3079a747e4fSDavid du Colombier 		}
3089a747e4fSDavid du Colombier 	}
3099a747e4fSDavid du Colombier 	nulldir(&nd);
3109a747e4fSDavid du Colombier 	nd.mtime = d->mtime;
3119a747e4fSDavid du Colombier 	if(dirfwstat(wfd, &nd) < 0)
3129a747e4fSDavid du Colombier 		fprint(2, "warning: cannot set mtime on %q\n", to);
3139a747e4fSDavid du Colombier 	close(wfd);
3149a747e4fSDavid du Colombier 	return 0;
3159a747e4fSDavid du Colombier }
3169a747e4fSDavid du Colombier 
3179a747e4fSDavid du Colombier int
metafile(char * path,Dir * d)3189a747e4fSDavid du Colombier metafile(char *path, Dir *d)
3199a747e4fSDavid du Colombier {
3209a747e4fSDavid du Colombier 	Dir nd;
3219a747e4fSDavid du Colombier 
3229a747e4fSDavid du Colombier 	nulldir(&nd);
3239a747e4fSDavid du Colombier 	nd.gid = d->gid;
3249a747e4fSDavid du Colombier 	nd.mode = d->mode;
3259a747e4fSDavid du Colombier 	if(douid)
3269a747e4fSDavid du Colombier 		nd.uid = d->uid;
3279a747e4fSDavid du Colombier 	if(dirwstat(path, &nd) < 0){
3289a747e4fSDavid du Colombier 		fprint(2, "dirwstat %q: %r\n", path);
3299a747e4fSDavid du Colombier 		return -1;
3309a747e4fSDavid du Colombier 	}
3319a747e4fSDavid du Colombier 	return 0;
3329a747e4fSDavid du Colombier }
333