xref: /plan9/sys/src/cmd/replica/applylog.c (revision 4d44ba9b9ee4246ddbd96c7fcaf0918ab92ab35a)
1 #include "all.h"
2 
3 #define	Nwork	16
4 
5 int localdirstat(char*, Dir*);
6 int ismatch(char*);
7 void conflict(char*, char*, ...);
8 void error(char*, ...);
9 int isdir(char*);
10 
11 void worker(int fdf, int fdt, char *from, char *to);
12 vlong	nextoff(void);
13 void	failure(void *, char *note);
14 
15 QLock	lk;
16 vlong	off;
17 
18 int errors;
19 int nconf;
20 int donothing;
21 char **conf;
22 int verbose;
23 char **match;
24 int nmatch;
25 int resolve;
26 int tempspool = 1;
27 int safeinstall = 1;
28 char *lroot;
29 char *rroot;
30 Db *clientdb;
31 int skip;
32 int douid;
33 char *mkname(char*, int, char*, char*);
34 char localbuf[10240];
35 char remotebuf[10240];
36 int copyfile(char*, char*, char*, Dir*, int, int*);
37 ulong maxnow;
38 int maxn;
39 char *timefile;
40 int timefd;
41 
42 Db *copyerr;
43 
44 void
45 readtimefile(void)
46 {
47 	int n;
48 	char buf[24];
49 
50 	if((timefd = open(timefile, ORDWR)) < 0
51 	&& (timefd = create(timefile, ORDWR|OEXCL, 0666)) < 0)
52 		return;
53 
54 	n = readn(timefd, buf, sizeof buf);
55 	if(n < sizeof buf)
56 		return;
57 
58 	maxnow = atoi(buf);
59 	maxn = atoi(buf+12);
60 }
61 
62 void
63 writetimefile(void)
64 {
65 	char buf[24+1];
66 
67 	snprint(buf, sizeof buf, "%11lud %11d ", maxnow, maxn);
68 	pwrite(timefd, buf, 24, 0);
69 }
70 
71 static void membogus(char**);
72 
73 void
74 addce(char *local)
75 {
76 	char e[ERRMAX];
77 	Dir d;
78 
79 	memset(&d, 0, sizeof d);
80 	rerrstr(e, sizeof e);
81 	d.name = atom(e);
82 	d.uid = "";
83 	d.gid = "";
84 	insertdb(copyerr, atom(local), &d);
85 }
86 
87 void
88 delce(char *local)
89 {
90 	removedb(copyerr, local);
91 }
92 
93 void
94 chat(char *f, ...)
95 {
96 	Fmt fmt;
97 	char buf[256];
98 	va_list arg;
99 
100 	if(!verbose)
101 		return;
102 
103 	fmtfdinit(&fmt, 1, buf, sizeof buf);
104 	va_start(arg, f);
105 	fmtvprint(&fmt, f, arg);
106 	va_end(arg);
107 	fmtfdflush(&fmt);
108 }
109 
110 void
111 usage(void)
112 {
113 	fprint(2, "usage: replica/applylog [-cnSstuv] [-T timefile] clientdb clientroot serverroot [path ...]\n");
114 	exits("usage");
115 }
116 
117 int
118 notexists(char *path)
119 {
120 	char buf[ERRMAX];
121 
122 	if(access(path, AEXIST) >= 0)
123 		return 0;
124 
125 	rerrstr(buf, sizeof buf);
126 	if(strstr(buf, "entry not found") || strstr(buf, "not exist"))
127 		return 1;
128 
129 	/* some other error, like network hangup */
130 	return 0;
131 }
132 
133 void
134 main(int argc, char **argv)
135 {
136 	char *f[10], *local, *name, *remote, *s, *t, verb;
137 	int fd, havedb, havelocal, k, n, nf, skip;
138 	ulong now;
139 	Biobuf bin;
140 	Dir dbd, ld, nd, rd;
141 	Avlwalk *w;
142 	Entry *e;
143 
144 	membogus(argv);
145 	quotefmtinstall();
146 	ARGBEGIN{
147 	case 's':
148 	case 'c':
149 		resolve = ARGC();
150 		break;
151 	case 'n':
152 		donothing = 1;
153 		verbose = 1;
154 		break;
155 	case 'S':
156 		safeinstall = 0;
157 		break;
158 	case 'T':
159 		timefile = EARGF(usage());
160 		break;
161 	case 't':
162 		tempspool = 0;
163 		break;
164 	case 'u':
165 		douid = 1;
166 		break;
167 	case 'v':
168 		verbose = 1;
169 		break;
170 	default:
171 		usage();
172 	}ARGEND
173 
174 	if(argc < 3)
175 		usage();
176 
177 	if(timefile)
178 		readtimefile();
179 
180 	lroot = argv[1];
181 	if(!isdir(lroot))
182 		sysfatal("bad local root directory");
183 	rroot = argv[2];
184 	if(!isdir(rroot))
185 		sysfatal("bad remote root directory");
186 
187 	if((clientdb = opendb(argv[0])) == nil)
188 		sysfatal("opendb %q: %r", argv[2]);
189 	match = argv+3;
190 	nmatch = argc-3;
191 
192 	copyerr = opendb(nil);
193 
194 	skip = 0;
195 	Binit(&bin, 0, OREAD);
196 	for(; s=Brdstr(&bin, '\n', 1); free(s)){
197 		t = estrdup(s);
198 		nf = tokenize(s, f, nelem(f));
199 		if(nf != 10 || strlen(f[2]) != 1){
200 			skip = 1;
201 			fprint(2, "warning: skipping bad log entry <%s>\n", t);
202 			free(t);
203 			continue;
204 		}
205 		free(t);
206 		now = strtoul(f[0], 0, 0);
207 		n = atoi(f[1]);
208 		verb = f[2][0];
209 		name = f[3];
210 		if(now < maxnow || (now==maxnow && n <= maxn))
211 			continue;
212 		if(!ismatch(name)){
213 			skip = 1;
214 			continue;
215 		}
216 		local = mkname(localbuf, sizeof localbuf, lroot, name);
217 		if(strcmp(f[4], "-") == 0)
218 			f[4] = f[3];
219 		remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
220 		rd.name = f[4];
221 		rd.mode = strtoul(f[5], 0, 8);
222 		rd.uid = f[6];
223 		rd.gid = f[7];
224 		rd.mtime = strtoul(f[8], 0, 10);
225 		rd.length = strtoll(f[9], 0, 10);
226 		havedb = finddb(clientdb, name, &dbd)>=0;
227 		havelocal = localdirstat(local, &ld)>=0;
228 
229 		switch(verb){
230 		case 'd':	/* delete file */
231 			delce(local);
232 			if(!havelocal)	/* doesn't exist; who cares? */
233 				break;
234 			if(!havedb){
235 				if(resolve == 's')
236 					goto DoRemove;
237 				else if(resolve == 'c')
238 					goto DoRemoveDb;
239 				conflict(name, "locally created; will not remove");
240 				skip = 1;
241 				continue;
242 			}
243 			assert(havelocal && havedb);
244 			if(dbd.mtime > rd.mtime)		/* we have a newer file than what was deleted */
245 				break;
246 			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* locally modified since we downloaded it */
247 				if(resolve == 's')
248 					goto DoRemove;
249 				else if(resolve == 'c')
250 					break;
251 				conflict(name, "locally modified; will not remove");
252 				skip = 1;
253 				continue;
254 			}
255 		    DoRemove:
256 			chat("d %q\n", name);
257 			if(donothing)
258 				break;
259 			if(remove(local) < 0){
260 				error("removing %q", name);
261 				skip = 1;
262 				continue;
263 			}
264 		    DoRemoveDb:
265 			removedb(clientdb, name);
266 			break;
267 
268 		case 'a':	/* add file */
269 			if(!havedb){
270 				if(!havelocal)
271 					goto DoCreate;
272 				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
273 					break;
274 				if(resolve == 's')
275 					goto DoCreate;
276 				else if(resolve == 'c')
277 					goto DoCreateDb;
278 				conflict(name, "locally created; will not overwrite");
279 				skip = 1;
280 				continue;
281 			}
282 			assert(havedb);
283 			if(dbd.mtime >= rd.mtime)	/* already created this file; ignore */
284 				break;
285 			if(havelocal){
286 				if((ld.mode&DMDIR) && (rd.mode&DMDIR))
287 					break;
288 				if(dbd.mtime==ld.mtime && dbd.length==ld.length)
289 					goto DoCreate;
290 				if(resolve=='s')
291 					goto DoCreate;
292 				else if(resolve == 'c')
293 					break;
294 				conflict(name, "locally modified; will not overwrite");
295 				skip = 1;
296 				continue;
297 			}
298 		    DoCreate:
299 			if(notexists(remote)){
300 				addce(local);
301 				/* no skip=1 */
302 				break;;
303 			}
304 			chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
305 			if(donothing)
306 				break;
307 			if(rd.mode&DMDIR){
308 				if((fd = create(local, OREAD, DMDIR)) < 0){
309 					error("mkdir %q: %r", name);
310 					skip = 1;
311 					continue;
312 				}
313 				nulldir(&nd);
314 				nd.mode = rd.mode;
315 				if(dirfwstat(fd, &nd) < 0)
316 					fprint(2, "warning: cannot set mode on %q\n", local);
317 				nulldir(&nd);
318 				nd.gid = rd.gid;
319 				if(dirfwstat(fd, &nd) < 0)
320 					fprint(2, "warning: cannot set gid on %q\n", local);
321 				if(douid){
322 					nulldir(&nd);
323 					nd.uid = rd.uid;
324 					if(dirfwstat(fd, &nd) < 0)
325 						fprint(2, "warning: cannot set uid on %q\n", local);
326 				}
327 				close(fd);
328 				rd.mtime = now;
329 			}else{
330 				if(copyfile(local, remote, name, &rd, 1, &k) < 0){
331 					if(k)
332 						addce(local);
333 					skip = 1;
334 					continue;
335 				}
336 			}
337 		    DoCreateDb:
338 			insertdb(clientdb, name, &rd);
339 			break;
340 
341 		case 'c':	/* change contents */
342 			if(!havedb){
343 				if(notexists(remote)){
344 					addce(local);
345 					/* no skip=1 */
346 					break;
347 				}
348 				if(resolve == 's')
349 					goto DoCopy;
350 				else if(resolve=='c')
351 					goto DoCopyDb;
352 				if(havelocal)
353 					conflict(name, "locally created; will not update");
354 				else
355 					conflict(name, "not replicated; will not update");
356 				skip = 1;
357 				continue;
358 			}
359 			if(dbd.mtime >= rd.mtime)		/* already have/had this version; ignore */
360 				break;
361 			if(!havelocal){
362 				if(notexists(remote)){
363 					addce(local);
364 					/* no skip=1 */
365 					break;
366 				}
367 				if(resolve == 's')
368 					goto DoCopy;
369 				else if(resolve == 'c')
370 					break;
371 				conflict(name, "locally removed; will not update");
372 				skip = 1;
373 				continue;
374 			}
375 			assert(havedb && havelocal);
376 			if(dbd.mtime != ld.mtime || dbd.length != ld.length){
377 				if(notexists(remote)){
378 					addce(local);
379 					/* no skip=1 */
380 					break;
381 				}
382 				if(resolve == 's')
383 					goto DoCopy;
384 				else if(resolve == 'c')
385 					break;
386 				conflict(name, "locally modified; will not update");
387 				skip = 1;
388 				continue;
389 			}
390 		    DoCopy:
391 			if(notexists(remote)){
392 				addce(local);
393 				/* no skip=1 */
394 				break;
395 			}
396 			chat("c %q\n", name);
397 			if(donothing)
398 				break;
399 			if(copyfile(local, remote, name, &rd, 0, &k) < 0){
400 				if(k)
401 					addce(local);
402 				skip = 1;
403 				continue;
404 			}
405 		    DoCopyDb:
406 			if(!havedb){
407 				if(havelocal)
408 					dbd = ld;
409 				else
410 					dbd = rd;
411 			}
412 			dbd.mtime = rd.mtime;
413 			dbd.length = rd.length;
414 			insertdb(clientdb, name, &dbd);
415 			break;
416 
417 		case 'm':	/* change metadata */
418 			if(!havedb){
419 				if(notexists(remote)){
420 					addce(local);
421 					/* no skip=1 */
422 					break;
423 				}
424 				if(resolve == 's')
425 					goto DoCreate;
426 				else if(resolve == 'c')
427 					goto DoMetaDb;
428 				if(havelocal)
429 					conflict(name, "locally created; will not update metadata");
430 				else
431 					conflict(name, "not replicated; will not update metadata");
432 				skip = 1;
433 				continue;
434 			}
435 			if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime)		/* have newer version; ignore */
436 				break;
437 			if((dbd.mode&DMDIR) && dbd.mtime > now)
438 				break;
439 			if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)	/* nothing to do */
440 				goto DoMetaDb;
441 			if(!havelocal){
442 				if(notexists(remote)){
443 					addce(local);
444 					/* no skip=1 */
445 					break;
446 				}
447 				if(resolve == 's')
448 					goto DoCreate;
449 				else if(resolve == 'c')
450 					break;
451 				conflict(name, "locally removed; will not update metadata");
452 				skip = 1;
453 				continue;
454 			}
455 			if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){	/* this check might be overkill */
456 				if(notexists(remote)){
457 					addce(local);
458 					/* no skip=1 */
459 					break;
460 				}
461 				if(resolve == 's')
462 					goto DoMeta;
463 				else if(resolve == 'c')
464 					break;
465 				conflict(name, "contents locally modified; will not update metadata to %s %s %luo",
466 					rd.uid, rd.gid, rd.mode);
467 				skip = 1;
468 				continue;
469 			}
470 			if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
471 				if(notexists(remote)){
472 					addce(local);
473 					/* no skip=1 */
474 					break;
475 				}
476 				if(resolve == 's')
477 					goto DoMeta;
478 				else if(resolve == 'c')
479 					break;
480 				conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
481 				skip = 1;
482 				continue;
483 			}
484 		    DoMeta:
485 			if(notexists(remote)){
486 				addce(local);
487 				/* no skip=1 */
488 				break;
489 			}
490 			chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
491 			if(donothing)
492 				break;
493 			nulldir(&nd);
494 			nd.gid = rd.gid;
495 			nd.mode = rd.mode;
496 			if(douid)
497 				nd.uid = rd.uid;
498 			if(dirwstat(local, &nd) < 0){
499 				error("dirwstat %q: %r", name);
500 				skip = 1;
501 				continue;
502 			}
503 		    DoMetaDb:
504 			if(!havedb){
505 				if(havelocal)
506 					dbd = ld;
507 				else
508 					dbd = rd;
509 			}
510 			if(dbd.mode&DMDIR)
511 				dbd.mtime = now;
512 			dbd.gid = rd.gid;
513 			dbd.mode = rd.mode;
514 			if(douid)
515 				dbd.uid = rd.uid;
516 			insertdb(clientdb, name, &dbd);
517 			break;
518 		}
519 		if(!skip && !donothing){
520 			maxnow = now;
521 			maxn = n;
522 		}
523 	}
524 
525 	w = avlwalk(copyerr->avl);
526 	while(e = (Entry*)avlnext(w))
527 		error("copying %q: %s\n", e->name, e->d.name);
528 
529 	if(timefile)
530 		writetimefile();
531 	if(nconf)
532 		exits("conflicts");
533 
534 	if(errors)
535 		exits("errors");
536 	exits(nil);
537 }
538 
539 
540 char*
541 mkname(char *buf, int nbuf, char *a, char *b)
542 {
543 	if(strlen(a)+strlen(b)+2 > nbuf)
544 		sysfatal("name too long");
545 
546 	strcpy(buf, a);
547 	if(a[strlen(a)-1] != '/')
548 		strcat(buf, "/");
549 	strcat(buf, b);
550 	return buf;
551 }
552 
553 int
554 isdir(char *s)
555 {
556 	ulong m;
557 	Dir *d;
558 
559 	if((d = dirstat(s)) == nil)
560 		return 0;
561 	m = d->mode;
562 	free(d);
563 	return (m&DMDIR) != 0;
564 }
565 
566 void
567 conflict(char *name, char *f, ...)
568 {
569 	char *s;
570 	va_list arg;
571 
572 	va_start(arg, f);
573 	s = vsmprint(f, arg);
574 	va_end(arg);
575 
576 	fprint(2, "%s: %s\n", name, s);
577 	free(s);
578 
579 	nconf++;
580 //	if(nconf%16 == 0)
581 //		conf = erealloc(conf, (nconf+16)*sizeof(conf[0]));
582 //	conf[nconf++] = estrdup(name);
583 }
584 
585 void
586 error(char *f, ...)
587 {
588 	char *s;
589 	va_list arg;
590 
591 	va_start(arg, f);
592 	s = vsmprint(f, arg);
593 	va_end(arg);
594 	fprint(2, "error: %s\n", s);
595 	free(s);
596 	errors = 1;
597 }
598 
599 int
600 ismatch(char *s)
601 {
602 	int i, len;
603 
604 	if(nmatch == 0)
605 		return 1;
606 	for(i=0; i<nmatch; i++){
607 		if(strcmp(s, match[i]) == 0)
608 			return 1;
609 		len = strlen(match[i]);
610 		if(strncmp(s, match[i], len) == 0 && s[len]=='/')
611 			return 1;
612 	}
613 	return 0;
614 }
615 
616 int
617 localdirstat(char *name, Dir *d)
618 {
619 	static Dir *d2;
620 
621 	free(d2);
622 	if((d2 = dirstat(name)) == nil)
623 		return -1;
624 	*d = *d2;
625 	return 0;
626 }
627 
628 enum { DEFB = 8192 };
629 
630 static int
631 copy1(int fdf, int fdt, char *from, char *to)
632 {
633 	int i, n, rv, pid[Nwork];
634 	Waitmsg *w;
635 
636 	n = 0;
637 	off = 0;
638 	for(i=0; i<Nwork; i++){
639 		switch(pid[n] = rfork(RFPROC|RFMEM)){
640 		case 0:
641 			notify(failure);
642 			worker(fdf, fdt, from, to);
643 		case -1:
644 			break;
645 		default:
646 			n++;
647 			break;
648 		}
649 	}
650 	if(n == 0){
651 		fprint(2, "cp: rfork: %r\n");
652 		return -1;
653 	}
654 
655 	rv = 0;
656 	while((w = wait()) != nil){
657 		if(w->msg[0]){
658 			rv = -1;
659 			for(i=0; i<n; i++)
660 				if(pid[i] > 0)
661 					postnote(PNPROC, pid[i], "failure");
662 		}
663 		free(w);
664 	}
665 	return rv;
666 }
667 
668 void
669 worker(int fdf, int fdt, char *from, char *to)
670 {
671 	char buf[DEFB], *bp;
672 	long len, n;
673 	vlong o;
674 
675 	len = sizeof(buf);
676 	bp = buf;
677 	o = nextoff();
678 
679 	while(n = pread(fdf, bp, len, o)){
680 		if(n < 0){
681 			fprint(2, "reading %s: %r\n", from);
682 			_exits("bad");
683 		}
684 		if(pwrite(fdt, buf, n, o) != n){
685 			fprint(2, "writing %s: %r\n", to);
686 			_exits("bad");
687 		}
688 		bp += n;
689 		o += n;
690 		len -= n;
691 		if(len == 0){
692 			len = sizeof buf;
693 			bp = buf;
694 			o = nextoff();
695 		}
696 	}
697 	_exits(nil);
698 }
699 
700 vlong
701 nextoff(void)
702 {
703 	vlong o;
704 
705 	qlock(&lk);
706 	o = off;
707 	off += DEFB;
708 	qunlock(&lk);
709 
710 	return o;
711 }
712 
713 void
714 failure(void*, char *note)
715 {
716 	if(strcmp(note, "failure") == 0)
717 		_exits(nil);
718 	noted(NDFLT);
719 }
720 
721 
722 static int
723 opentemp(char *template)
724 {
725 	int fd, i;
726 	char *p;
727 
728 	p = estrdup(template);
729 	fd = -1;
730 	for(i=0; i<10; i++){
731 		mktemp(p);
732 		if((fd=create(p, ORDWR|OEXCL|ORCLOSE, 0000)) >= 0)
733 			break;
734 		strcpy(p, template);
735 	}
736 	if(fd < 0)
737 		return -1;
738 	strcpy(template, p);
739 	free(p);
740 	return fd;
741 }
742 
743 int
744 copyfile(char *local, char *remote, char *name, Dir *d, int dowstat, int *printerror)
745 {
746 	Dir *d0, *d1, *dl;
747 	Dir nd;
748 	int rfd, tfd, wfd, didcreate;
749 	char tmp[32], *p, *safe;
750 	char err[ERRMAX];
751 
752 Again:
753 	*printerror = 0;
754 	if((rfd = open(remote, OREAD)) < 0)
755 		return -1;
756 
757 	d0 = dirfstat(rfd);
758 	if(d0 == nil){
759 		close(rfd);
760 		return -1;
761 	}
762 	*printerror = 1;
763 	if(!tempspool){
764 		tfd = rfd;
765 		goto DoCopy;
766 	}
767 	strcpy(tmp, "/tmp/replicaXXXXXXXX");
768 	tfd = opentemp(tmp);
769 	if(tfd < 0){
770 		close(rfd);
771 		free(d0);
772 		return -1;
773 	}
774 	if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){
775 		close(rfd);
776 		close(tfd);
777 		free(d0);
778 		return -1;
779 	}
780 	close(rfd);
781 	if(d0->qid.path != d1->qid.path
782 	|| d0->qid.vers != d1->qid.vers
783 	|| d0->mtime != d1->mtime
784 	|| d0->length != d1->length){
785 		/* file changed underfoot; go around again */
786 		close(tfd);
787 		free(d0);
788 		free(d1);
789 		goto Again;
790 	}
791 	free(d1);
792 	if(seek(tfd, 0, 0) != 0){
793 		close(tfd);
794 		free(d0);
795 		return -1;
796 	}
797 
798 DoCopy:
799 	/*
800 	 * clumsy but important hack to do safeinstall-like installs.
801 	 */
802 	p = strchr(name, '/');
803 	if(safeinstall && p && strncmp(p, "/bin/", 5) == 0 && access(local, AEXIST) >= 0){
804 		/*
805 		 * remove bin/_targ
806 		 */
807 		safe = emalloc(strlen(local)+2);
808 		strcpy(safe, local);
809 		p = strrchr(safe, '/')+1;
810 		memmove(p+1, p, strlen(p)+1);
811 		p[0] = '_';
812 		remove(safe);	/* ignore failure */
813 
814 		/*
815 		 * rename bin/targ to bin/_targ
816 		 */
817 		nulldir(&nd);
818 		nd.name = p;
819 		if(dirwstat(local, &nd) < 0)
820 			fprint(2, "warning: rename %s to %s: %r\n", local, p);
821 	}
822 
823 	didcreate = 0;
824 	if((dl = dirstat(local)) == nil){
825 		if((wfd = create(local, OWRITE, 0)) >= 0){
826 			didcreate = 1;
827 			goto okay;
828 		}
829 		goto err;
830 	}else{
831 		if((wfd = open(local, OTRUNC|OWRITE)) >= 0)
832 			goto okay;
833 		rerrstr(err, sizeof err);
834 		if(strstr(err, "permission") == nil)
835 			goto err;
836 		nulldir(&nd);
837 		/*
838 		 * Assume the person running pull is in the appropriate
839 		 * groups.  We could set 0666 instead, but I'm worried
840 		 * about leaving the file world-readable or world-writable
841 		 * when it shouldn't be.
842 		 */
843 		nd.mode = dl->mode | 0660;
844 		if(nd.mode == dl->mode)
845 			goto err;
846 		if(dirwstat(local, &nd) < 0)
847 			goto err;
848 		if((wfd = open(local, OTRUNC|OWRITE)) >= 0){
849 			nd.mode = dl->mode;
850 			if(dirfwstat(wfd, &nd) < 0)
851 				fprint(2, "warning: set mode on %s to 0660 to open; cannot set back to %luo: %r\n", local, nd.mode);
852 			goto okay;
853 		}
854 		nd.mode = dl->mode;
855 		if(dirwstat(local, &nd) < 0)
856 			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);
857 		goto err;
858 	}
859 
860 err:
861 	close(tfd);
862 	free(d0);
863 	free(dl);
864 	return -1;
865 
866 okay:
867 	free(dl);
868 	if(copy1(tfd, wfd, tmp, local) < 0){
869 		close(tfd);
870 		close(wfd);
871 		free(d0);
872 		return -1;
873 	}
874 	close(tfd);
875 	if(didcreate || dowstat){
876 		nulldir(&nd);
877 		nd.mode = d->mode;
878 		if(dirfwstat(wfd, &nd) < 0)
879 			fprint(2, "warning: cannot set mode on %s\n", local);
880 		nulldir(&nd);
881 		nd.gid = d->gid;
882 		if(dirfwstat(wfd, &nd) < 0)
883 			fprint(2, "warning: cannot set gid on %s\n", local);
884 		if(douid){
885 			nulldir(&nd);
886 			nd.uid = d->uid;
887 			if(dirfwstat(wfd, &nd) < 0)
888 				fprint(2, "warning: cannot set uid on %s\n", local);
889 		}
890 	}
891 	d->mtime = d0->mtime;
892 	d->length = d0->length;
893 	nulldir(&nd);
894 	nd.mtime = d->mtime;
895 	if(dirfwstat(wfd, &nd) < 0)
896 		fprint(2, "warning: cannot set mtime on %s\n", local);
897 	free(d0);
898 
899 	close(wfd);
900 	return 0;
901 }
902 
903 /*
904  * Applylog might try to overwrite itself.
905  * To avoid problems with this, we copy ourselves
906  * into /tmp and then re-exec.
907  */
908 char *rmargv0;
909 
910 static void
911 rmself(void)
912 {
913 	remove(rmargv0);
914 }
915 
916 static int
917 genopentemp(char *template, int mode, int perm)
918 {
919 	int fd, i;
920 	char *p;
921 
922 	p = estrdup(template);
923 	fd = -1;
924 	for(i=0; i<10; i++){
925 		mktemp(p);
926 		if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
927 			break;
928 		strcpy(p, template);
929 	}
930 	if(fd < 0)
931 		sysfatal("could not create temporary file");
932 
933 	strcpy(template, p);
934 	free(p);
935 
936 	return fd;
937 }
938 
939 static void
940 membogus(char **argv)
941 {
942 	int n, fd, wfd;
943 	char template[50], buf[1024];
944 
945 	if(strncmp(argv[0], "/tmp/_applylog_", 1+3+1+1+8+1)==0) {
946 		rmargv0 = argv[0];
947 		atexit(rmself);
948 		return;
949 	}
950 
951 	if((fd = open(argv[0], OREAD)) < 0)
952 		return;
953 
954 	strcpy(template, "/tmp/_applylog_XXXXXX");
955 	if((wfd = genopentemp(template, OWRITE, 0700)) < 0)
956 		return;
957 
958 	while((n = read(fd, buf, sizeof buf)) > 0)
959 		if(write(wfd, buf, n) != n)
960 			goto Error;
961 
962 	if(n != 0)
963 		goto Error;
964 
965 	close(fd);
966 	close(wfd);
967 
968 	argv[0] = template;
969 	exec(template, argv);
970 	fprint(2, "exec error %r\n");
971 
972 Error:
973 	close(fd);
974 	close(wfd);
975 	remove(template);
976 	return;
977 }
978