xref: /plan9/sys/src/cmd/replica/applychanges.c (revision 1066d6debf4f3ce80fbab98c906650d920c13a7a)
1 /*
2  * push changes from client to server.
3  */
4 #include "all.h"
5 
6 int douid;
7 Db *db;
8 char **x;
9 int nx;
10 int justshow;
11 int verbose;
12 int conflicts;
13 char newpath[10000];
14 char oldpath[10000];
15 char *clientroot;
16 char *serverroot;
17 int copyfile(char*, char*, Dir*, int);
18 int metafile(char*, Dir*);
19 char **match;
20 int nmatch;
21 
22 int
ismatch(char * s)23 ismatch(char *s)
24 {
25 	int i, len;
26 
27 	if(nmatch == 0)
28 		return 1;
29 	for(i=0; i<nmatch; i++){
30 		if(strcmp(s, match[i]) == 0)
31 			return 1;
32 		len = strlen(match[i]);
33 		if(strncmp(s, match[i], len) == 0 && s[len]=='/')
34 			return 1;
35 	}
36 	return 0;
37 }
38 
39 void
xlog(char c,char * path,Dir * d)40 xlog(char c, char *path, Dir *d)
41 {
42 	if(!verbose)
43 		return;
44 	print("%c %s %luo %s %s %lud\n", c, path, d->mode, d->uid, d->gid, d->mtime);
45 }
46 
47 void
walk(char * new,char * old,Dir * pd,void *)48 walk(char *new, char *old, Dir *pd, void*)
49 {
50 	int i, len;
51 	Dir od, d;
52 	static Dir *xd;
53 
54 	new = unroot(new, "/");
55 	old = unroot(old, serverroot);
56 
57 	if(!ismatch(new))
58 		return;
59 	if(xd != nil){
60 		free(xd);
61 		xd = nil;
62 	}
63 
64 	for(i=0; i<nx; i++){
65 		if(strcmp(new, x[i]) == 0)
66 			return;
67 		len = strlen(x[i]);
68 		if(strncmp(new, x[i], len)==0 && new[len]=='/')
69 			return;
70 	}
71 
72 	d = *pd;
73 	d.name = old;
74 	memset(&od, 0, sizeof od);
75 	snprint(newpath, sizeof newpath, "%s/%s", clientroot, new);
76 	snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, old);
77 
78 	xd = dirstat(oldpath);
79 	if(markdb(db, new, &od) < 0){
80 		if(xd != nil){
81 			print("x %s create/create conflict\n", new);
82 			conflicts = 1;
83 			return;
84 		}
85 		xlog('a', new, &d);
86 		d.muid = "mark";	/* mark bit */
87 		if(!justshow){
88 			if(copyfile(newpath, oldpath, &d, 1) == 0)
89 				insertdb(db, new, &d);
90 		}
91 	}else{
92 		if((d.mode&DMDIR)==0 && od.mtime!=d.mtime){
93 			if(xd==nil){
94 				print("%s update/remove conflict\n", new);
95 				conflicts = 1;
96 				return;
97 			}
98 			if(xd->mtime != od.mtime){
99 				print("%s update/update conflict\n", new);
100 				conflicts = 1;
101 				return;
102 			}
103 			od.mtime = d.mtime;
104 			od.muid = "mark";
105 			xlog('c', new, &od);
106 			if(!justshow){
107 				if(copyfile(newpath, oldpath, &od, 0) == 0)
108 					insertdb(db, new, &od);
109 			}
110 		}
111 		if((douid&&strcmp(od.uid,d.uid)!=0)
112 		|| strcmp(od.gid,d.gid)!=0
113 		|| od.mode!=d.mode){
114 			if(xd==nil){
115 				print("%s metaupdate/remove conflict\n", new);
116 				conflicts = 1;
117 				return;
118 			}
119 			if((douid&&strcmp(od.uid,xd->uid)!=0)
120 			|| strcmp(od.uid,xd->gid)!=0
121 			|| od.mode!=xd->mode){
122 				print("%s metaupdate/metaupdate conflict\n", new);
123 				conflicts = 1;
124 				return;
125 			}
126 			if(douid)
127 				od.uid = d.uid;
128 			od.gid = d.gid;
129 			od.mode = d.mode;
130 			od.muid = "mark";
131 			xlog('m', new, &od);
132 			if(!justshow){
133 				if(metafile(oldpath, &od) == 0)
134 					insertdb(db, new, &od);
135 			}
136 		}
137 	}
138 }
139 
140 void
usage(void)141 usage(void)
142 {
143 	fprint(2, "usage: replica/applychanges [-nuv] [-p proto] [-x path]... clientdb clientroot serverroot [path ...]\n");
144 	exits("usage");
145 }
146 
147 void
main(int argc,char ** argv)148 main(int argc, char **argv)
149 {
150 	char *proto;
151 	Avlwalk *w;
152 	Dir *xd, d;
153 	Entry *e;
154 
155 	quotefmtinstall();
156 	proto = "/sys/lib/sysconfig/proto/allproto";
157 	ARGBEGIN{
158 	case 'n':
159 		justshow = 1;
160 		verbose = 1;
161 		break;
162 	case 'p':
163 		proto = EARGF(usage());
164 		break;
165 	case 'u':
166 		douid = 1;
167 		break;
168 	case 'v':
169 		verbose = 1;
170 		break;
171 	case 'x':
172 		if(nx%16 == 0)
173 			x = erealloc(x, (nx+16)*sizeof(x[0]));
174 		x[nx++] = EARGF(usage());
175 		break;
176 	default:
177 		usage();
178 	}ARGEND
179 
180 	if(argc < 3)
181 		usage();
182 
183 	db = opendb(argv[0]);
184 	clientroot = argv[1];
185 	serverroot = argv[2];
186 	match = argv+3;
187 	nmatch = argc-3;
188 
189 
190 	if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0)
191 		sysfatal("rdproto: %r");
192 
193 	w = avlwalk(db->avl);
194 	while(e = (Entry*)avlprev(w)){
195 		if(!ismatch(e->name))
196 			continue;
197 		if(!e->d.mark){		/* not visited during walk */
198 			snprint(newpath, sizeof newpath, "%s/%s", clientroot, e->name);
199 			snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, e->d.name);
200 			xd = dirstat(oldpath);
201 			if(xd == nil){
202 				removedb(db, e->name);
203 				continue;
204 			}
205 			if(xd->mtime != e->d.mtime && (e->d.mode&xd->mode&DMDIR)==0){
206 				print("x %q remove/update conflict\n", e->name);
207 				free(xd);
208 				continue;
209 			}
210 			memset(&d, 0, sizeof d);
211 			d.name = e->d.name;
212 			d.uid = e->d.uid;
213 			d.gid = e->d.gid;
214 			d.mtime = e->d.mtime;
215 			d.mode = e->d.mode;
216 			xlog('d', e->name, &d);
217 			if(!justshow){
218 				if(remove(oldpath) == 0)
219 					removedb(db, e->name);
220 			}
221 			free(xd);
222 		}
223 	}
224 
225 	if(conflicts)
226 		exits("conflicts");
227 	exits(nil);
228 }
229 
230 enum { DEFB = 8192 };
231 
232 static int
copy1(int fdf,int fdt,char * from,char * to)233 copy1(int fdf, int fdt, char *from, char *to)
234 {
235 	char buf[DEFB];
236 	long n, n1, rcount;
237 	int rv;
238 	char err[ERRMAX];
239 
240 	/* clear any residual error */
241 	err[0] = '\0';
242 	errstr(err, ERRMAX);
243 	rv = 0;
244 	for(rcount=0;; rcount++) {
245 		n = read(fdf, buf, DEFB);
246 		if(n <= 0)
247 			break;
248 		n1 = write(fdt, buf, n);
249 		if(n1 != n) {
250 			fprint(2, "error writing %q: %r\n", to);
251 			rv = -1;
252 			break;
253 		}
254 	}
255 	if(n < 0) {
256 		fprint(2, "error reading %q: %r\n", from);
257 		rv = -1;
258 	}
259 	return rv;
260 }
261 
262 int
copyfile(char * from,char * to,Dir * d,int dowstat)263 copyfile(char *from, char *to, Dir *d, int dowstat)
264 {
265 	Dir nd;
266 	int rfd, wfd, didcreate;
267 
268 	if((rfd = open(from, OREAD)) < 0)
269 		return -1;
270 
271 	didcreate = 0;
272 	if(d->mode&DMDIR){
273 		if((wfd = create(to, OREAD, DMDIR)) < 0){
274 			fprint(2, "mkdir %q: %r\n", to);
275 			close(rfd);
276 			return -1;
277 		}
278 	}else{
279 		if((wfd = open(to, OTRUNC|OWRITE)) < 0){
280 			if((wfd = create(to, OWRITE, 0)) < 0){
281 				close(rfd);
282 				return -1;
283 			}
284 			didcreate = 1;
285 		}
286 		if(copy1(rfd, wfd, from, to) < 0){
287 			close(rfd);
288 			close(wfd);
289 			return -1;
290 		}
291 	}
292 	close(rfd);
293 	if(didcreate || dowstat){
294 		nulldir(&nd);
295 		nd.mode = d->mode;
296 		if(dirfwstat(wfd, &nd) < 0)
297 			fprint(2, "warning: cannot set mode on %q\n", to);
298 		nulldir(&nd);
299 		nd.gid = d->gid;
300 		if(dirfwstat(wfd, &nd) < 0)
301 			fprint(2, "warning: cannot set gid on %q\n", to);
302 		if(douid){
303 			nulldir(&nd);
304 			nd.uid = d->uid;
305 			if(dirfwstat(wfd, &nd) < 0)
306 				fprint(2, "warning: cannot set uid on %q\n", to);
307 		}
308 	}
309 	nulldir(&nd);
310 	nd.mtime = d->mtime;
311 	if(dirfwstat(wfd, &nd) < 0)
312 		fprint(2, "warning: cannot set mtime on %q\n", to);
313 	close(wfd);
314 	return 0;
315 }
316 
317 int
metafile(char * path,Dir * d)318 metafile(char *path, Dir *d)
319 {
320 	Dir nd;
321 
322 	nulldir(&nd);
323 	nd.gid = d->gid;
324 	nd.mode = d->mode;
325 	if(douid)
326 		nd.uid = d->uid;
327 	if(dirwstat(path, &nd) < 0){
328 		fprint(2, "dirwstat %q: %r\n", path);
329 		return -1;
330 	}
331 	return 0;
332 }
333