xref: /plan9/sys/src/cmd/replica/applychanges.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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
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
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
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 			xlog('c', new, &od);
105 			if(!justshow){
106 				if(copyfile(new, old, &od, 0) == 0)
107 					insertdb(db, new, &od);
108 			}
109 		}
110 		if((douid&&strcmp(od.uid,d.uid)!=0)
111 		|| strcmp(od.gid,d.gid)!=0
112 		|| od.mode!=d.mode){
113 			if(xd==nil){
114 				print("%s metaupdate/remove conflict\n", new);
115 				conflicts = 1;
116 				return;
117 			}
118 			if((douid&&strcmp(od.uid,xd->uid)!=0)
119 			|| strcmp(od.uid,xd->gid)!=0
120 			|| od.mode!=xd->mode){
121 				print("%s metaupdate/metaupdate conflict\n", new);
122 				conflicts = 1;
123 				return;
124 			}
125 			if(douid)
126 				od.uid = d.uid;
127 			od.gid = d.gid;
128 			od.mode = d.mode;
129 			xlog('m', new, &od);
130 			if(!justshow){
131 				if(metafile(oldpath, &od) == 0)
132 					insertdb(db, new, &od);
133 			}
134 		}
135 	}
136 }
137 
138 void
139 usage(void)
140 {
141 	fprint(2, "usage: replica/applychanges [-p proto] [-r root] [-t now n] [-u uid] [-x path]... clientdb [path ...]\n");
142 	exits("usage");
143 }
144 
145 void
146 main(int argc, char **argv)
147 {
148 	char *proto;
149 	Avlwalk *w;
150 	Dir *xd, d;
151 	Entry *e;
152 
153 	quotefmtinstall();
154 	proto = "/sys/lib/sysconfig/proto/allproto";
155 	ARGBEGIN{
156 	case 'n':
157 		justshow = 1;
158 		verbose = 1;
159 		break;
160 	case 'p':
161 		proto = EARGF(usage());
162 		break;
163 	case 'u':
164 		douid = 1;
165 		break;
166 	case 'v':
167 		verbose = 1;
168 		break;
169 	case 'x':
170 		if(nx%16 == 0)
171 			x = erealloc(x, (nx+16)*sizeof(x[0]));
172 		x[nx++] = EARGF(usage());
173 		break;
174 	default:
175 		usage();
176 	}ARGEND
177 
178 	if(argc < 3)
179 		usage();
180 
181 	db = opendb(argv[0]);
182 	clientroot = argv[1];
183 	serverroot = argv[2];
184 	match = argv+3;
185 	nmatch = argc-3;
186 
187 
188 	if(revrdproto(proto, clientroot, serverroot, walk, nil, nil) < 0)
189 		sysfatal("rdproto: %r");
190 
191 	w = avlwalk(db->avl);
192 	while(e = (Entry*)avlnext(w)){
193 		if(!ismatch(e->name))
194 			continue;
195 		if(!e->d.mark){		/* not visited during walk */
196 			snprint(newpath, sizeof newpath, "%s/%s", clientroot, e->name);
197 			snprint(oldpath, sizeof oldpath, "%s/%s", serverroot, e->d.name);
198 			xd = dirstat(oldpath);
199 			if(xd == nil){
200 				removedb(db, e->name);
201 				continue;
202 			}
203 			if(xd->mtime != e->d.mtime){
204 				print("x %q remove/update conflict\n", e->name);
205 				free(xd);
206 				continue;
207 			}
208 			memset(&d, 0, sizeof d);
209 			d.name = e->d.name;
210 			d.uid = e->d.uid;
211 			d.gid = e->d.gid;
212 			d.mtime = e->d.mtime;
213 			d.mode = e->d.mode;
214 			xlog('d', e->name, &d);
215 			if(!justshow){
216 				if(remove(oldpath) == 0)
217 					removedb(db, e->name);
218 			}
219 		}
220 	}
221 
222 	if(conflicts)
223 		exits("conflicts");
224 	exits(nil);
225 }
226 
227 enum { DEFB = 8192 };
228 
229 static int
230 copy1(int fdf, int fdt, char *from, char *to)
231 {
232 	char buf[DEFB];
233 	long n, n1, rcount;
234 	int rv;
235 	char err[ERRMAX];
236 
237 	/* clear any residual error */
238 	err[0] = '\0';
239 	errstr(err, ERRMAX);
240 	rv = 0;
241 	for(rcount=0;; rcount++) {
242 		n = read(fdf, buf, DEFB);
243 		if(n <= 0)
244 			break;
245 		n1 = write(fdt, buf, n);
246 		if(n1 != n) {
247 			fprint(2, "error writing %q: %r\n", to);
248 			rv = -1;
249 			break;
250 		}
251 	}
252 	if(n < 0) {
253 		fprint(2, "error reading %q: %r\n", from);
254 		rv = -1;
255 	}
256 	return rv;
257 }
258 
259 int
260 copyfile(char *from, char *to, Dir *d, int dowstat)
261 {
262 	Dir nd;
263 	int rfd, wfd, didcreate;
264 
265 	if((rfd = open(from, OREAD)) < 0)
266 		return -1;
267 
268 	didcreate = 0;
269 	if((wfd = open(to, OTRUNC|OWRITE)) < 0){
270 		if((wfd = create(to, OWRITE, 0)) < 0){
271 			close(rfd);
272 			return -1;
273 		}
274 		didcreate = 1;
275 	}
276 	if(copy1(rfd, wfd, from, to) < 0){
277 		close(rfd);
278 		close(wfd);
279 		return -1;
280 	}
281 	if(didcreate || dowstat){
282 		nulldir(&nd);
283 		nd.mode = d->mode;
284 		if(dirfwstat(wfd, &nd) < 0)
285 			fprint(2, "warning: cannot set mode on %q\n", to);
286 		nulldir(&nd);
287 		nd.gid = d->gid;
288 		if(dirfwstat(wfd, &nd) < 0)
289 			fprint(2, "warning: cannot set gid on %q\n", to);
290 		if(douid){
291 			nulldir(&nd);
292 			nd.uid = d->uid;
293 			if(dirfwstat(wfd, &nd) < 0)
294 				fprint(2, "warning: cannot set uid on %q\n", to);
295 		}
296 	}
297 	nulldir(&nd);
298 	nd.mtime = d->mtime;
299 	if(dirfwstat(wfd, &nd) < 0)
300 		fprint(2, "warning: cannot set mtime on %q\n", to);
301 	close(wfd);
302 	return 0;
303 }
304 
305 int
306 metafile(char *path, Dir *d)
307 {
308 	Dir nd;
309 
310 	nulldir(&nd);
311 	nd.gid = d->gid;
312 	nd.mode = d->mode;
313 	if(douid)
314 		nd.uid = d->uid;
315 	if(dirwstat(path, &nd) < 0){
316 		fprint(2, "dirwstat %q: %r\n", path);
317 		return -1;
318 	}
319 	return 0;
320 }
321