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 int verbose;
22 char **match;
23 int nmatch;
24 int tempspool = 1;
25 int safeinstall = 1;
26 char *lroot;
27 char *rroot;
28 Db *clientdb;
29 int skip;
30 int douid;
31 char *mkname(char*, int, char*, char*);
32 char localbuf[10240];
33 char remotebuf[10240];
34 int copyfile(char*, char*, char*, Dir*, int, int*);
35 ulong maxnow;
36 int maxn;
37 char *timefile;
38 int timefd;
39 int samecontents(char*, char*);
40
41 Db *copyerr;
42
43 typedef struct Res Res;
44 struct Res
45 {
46 char c;
47 char *name;
48 };
49
50 Res *res;
51 int nres;
52
53 void
addresolve(int c,char * name)54 addresolve(int c, char *name)
55 {
56 if(name[0] == '/')
57 name++;
58 res = erealloc(res, (nres+1)*sizeof res[0]);
59 res[nres].c = c;
60 res[nres].name = name;
61 nres++;
62 }
63
64 int
resolve(char * name)65 resolve(char *name)
66 {
67 int i, len;
68
69 for(i=0; i<nres; i++){
70 len = strlen(res[i].name);
71 if(len == 0)
72 return res[i].c;
73 if(strncmp(name, res[i].name, len) == 0 && (name[len]=='/' || name[len] == 0))
74 return res[i].c;
75 }
76 return '?';
77 }
78
79 void
readtimefile(void)80 readtimefile(void)
81 {
82 int n;
83 char buf[24];
84
85 if((timefd = open(timefile, ORDWR)) < 0
86 && (timefd = create(timefile, ORDWR|OEXCL, 0666)) < 0)
87 return;
88
89 n = readn(timefd, buf, sizeof buf);
90 if(n < sizeof buf)
91 return;
92
93 maxnow = atoi(buf);
94 maxn = atoi(buf+12);
95 }
96
97 void
writetimefile(void)98 writetimefile(void)
99 {
100 char buf[24+1];
101
102 snprint(buf, sizeof buf, "%11lud %11d ", maxnow, maxn);
103 pwrite(timefd, buf, 24, 0);
104 }
105
106 static void membogus(char**);
107
108 void
addce(char * local)109 addce(char *local)
110 {
111 char e[ERRMAX];
112 Dir d;
113
114 memset(&d, 0, sizeof d);
115 rerrstr(e, sizeof e);
116 d.name = atom(e);
117 d.uid = "";
118 d.gid = "";
119 insertdb(copyerr, atom(local), &d);
120 }
121
122 void
delce(char * local)123 delce(char *local)
124 {
125 removedb(copyerr, local);
126 }
127
128 void
chat(char * f,...)129 chat(char *f, ...)
130 {
131 Fmt fmt;
132 char buf[256];
133 va_list arg;
134
135 if(!verbose)
136 return;
137
138 fmtfdinit(&fmt, 1, buf, sizeof buf);
139 va_start(arg, f);
140 fmtvprint(&fmt, f, arg);
141 va_end(arg);
142 fmtfdflush(&fmt);
143 }
144
145 void
usage(void)146 usage(void)
147 {
148 fprint(2, "usage: replica/applylog [-cnSstuv] [-T timefile] clientdb clientroot serverroot [path ...]\n");
149 exits("usage");
150 }
151
152 int
notexists(char * path)153 notexists(char *path)
154 {
155 char buf[ERRMAX];
156
157 if(access(path, AEXIST) >= 0)
158 return 0;
159
160 rerrstr(buf, sizeof buf);
161 if(strstr(buf, "entry not found") || strstr(buf, "not exist"))
162 return 1;
163
164 /* some other error, like network hangup */
165 return 0;
166 }
167
168 int
prstopped(int skip,char * name)169 prstopped(int skip, char *name)
170 {
171 if(!skip) {
172 fprint(2, "stopped updating log apply time because of %s\n",
173 name);
174 skip = 1;
175 }
176 return skip;
177 }
178
179 void
main(int argc,char ** argv)180 main(int argc, char **argv)
181 {
182 char *f[10], *local, *name, *remote, *s, *t, verb;
183 int fd, havedb, havelocal, i, k, n, nf, resolve1, skip;
184 int checkedmatch1, checkedmatch2,
185 checkedmatch3, checkedmatch4;
186 ulong now;
187 Biobuf bin;
188 Dir dbd, ld, nd, rd;
189 Avlwalk *w;
190 Entry *e;
191
192 membogus(argv);
193 quotefmtinstall();
194 ARGBEGIN{
195 case 's':
196 case 'c':
197 i = ARGC();
198 addresolve(i, EARGF(usage()));
199 break;
200 case 'n':
201 donothing = 1;
202 verbose = 1;
203 break;
204 case 'S':
205 safeinstall = 0;
206 break;
207 case 'T':
208 timefile = EARGF(usage());
209 break;
210 case 't':
211 tempspool = 0;
212 break;
213 case 'u':
214 douid = 1;
215 break;
216 case 'v':
217 verbose++;
218 break;
219 default:
220 usage();
221 }ARGEND
222
223 if(argc < 3)
224 usage();
225
226 if(timefile)
227 readtimefile();
228
229 lroot = argv[1];
230 if(!isdir(lroot))
231 sysfatal("bad local root directory");
232 rroot = argv[2];
233 if(!isdir(rroot))
234 sysfatal("bad remote root directory");
235
236 match = argv+3;
237 nmatch = argc-3;
238 for(i=0; i<nmatch; i++)
239 if(match[i][0] == '/')
240 match[i]++;
241
242 if((clientdb = opendb(argv[0])) == nil)
243 sysfatal("opendb %q: %r", argv[2]);
244
245 copyerr = opendb(nil);
246
247 skip = 0;
248 Binit(&bin, 0, OREAD);
249 for(; s=Brdstr(&bin, '\n', 1); free(s)){
250 t = estrdup(s);
251 nf = tokenize(s, f, nelem(f));
252 if(nf != 10 || strlen(f[2]) != 1){
253 skip = 1;
254 fprint(2, "warning: skipping bad log entry <%s>\n", t);
255 free(t);
256 continue;
257 }
258 free(t);
259 now = strtoul(f[0], 0, 0);
260 n = atoi(f[1]);
261 verb = f[2][0];
262 name = f[3];
263 if(now < maxnow || (now==maxnow && n <= maxn))
264 continue;
265 local = mkname(localbuf, sizeof localbuf, lroot, name);
266 if(strcmp(f[4], "-") == 0)
267 f[4] = f[3];
268 remote = mkname(remotebuf, sizeof remotebuf, rroot, f[4]);
269 rd.name = f[4];
270 rd.mode = strtoul(f[5], 0, 8);
271 rd.uid = f[6];
272 rd.gid = f[7];
273 rd.mtime = strtoul(f[8], 0, 10);
274 rd.length = strtoll(f[9], 0, 10);
275 havedb = finddb(clientdb, name, &dbd)>=0;
276 havelocal = localdirstat(local, &ld)>=0;
277
278 resolve1 = resolve(name);
279
280 /*
281 * if(!ismatch(name)){
282 * skip = 1;
283 * continue;
284 * }
285 *
286 * This check used to be right here, but we want
287 * the time to be able to move forward past entries
288 * that don't match and have already been applied.
289 * So now every path below must checked !ismatch(name)
290 * before making any changes to the local file
291 * system. The fake variable checkedmatch
292 * tracks whether !ismatch(name) has been checked.
293 * If the compiler doesn't produce any used/set
294 * warnings, then all the paths should be okay.
295 * Even so, we have the asserts to fall back on.
296 */
297 switch(verb){
298 case 'd': /* delete file */
299 delce(local);
300 if(!havelocal) /* doesn't exist; who cares? */
301 break;
302 if(access(remote, AEXIST) >= 0) /* got recreated! */
303 break;
304 if(!ismatch(name)){
305 skip = prstopped(skip, name);
306 continue;
307 }
308 SET(checkedmatch1);
309 if(!havedb){
310 if(resolve1 == 's')
311 goto DoRemove;
312 else if(resolve1 == 'c')
313 goto DoRemoveDb;
314 conflict(name, "locally created; will not remove");
315 skip = 1;
316 continue;
317 }
318 assert(havelocal && havedb);
319 if(dbd.mtime > rd.mtime) /* we have a newer file than what was deleted */
320 break;
321 if(samecontents(local, remote) > 0){ /* going to get recreated */
322 chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
323 break;
324 }
325 if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* locally modified since we downloaded it */
326 if(resolve1 == 's')
327 goto DoRemove;
328 else if(resolve1 == 'c')
329 break;
330 conflict(name, "locally modified; will not remove");
331 skip = 1;
332 continue;
333 }
334 DoRemove:
335 USED(checkedmatch1);
336 assert(ismatch(name));
337 chat("d %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
338 if(donothing)
339 break;
340 if(remove(local) < 0){
341 error("removing %q: %r", name);
342 skip = 1;
343 continue;
344 }
345 DoRemoveDb:
346 USED(checkedmatch1);
347 assert(ismatch(name));
348 removedb(clientdb, name);
349 break;
350
351 case 'a': /* add file */
352 if(!havedb){
353 if(!ismatch(name)){
354 skip = prstopped(skip, name);
355 continue;
356 }
357 SET(checkedmatch2);
358 if(!havelocal)
359 goto DoCreate;
360 if((ld.mode&DMDIR) && (rd.mode&DMDIR))
361 break;
362 if(samecontents(local, remote) > 0){
363 chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
364 goto DoCreateDb;
365 }
366 if(resolve1 == 's')
367 goto DoCreate;
368 else if(resolve1 == 'c')
369 goto DoCreateDb;
370 conflict(name, "locally created; will not overwrite");
371 skip = 1;
372 continue;
373 }
374 assert(havedb);
375 if(dbd.mtime >= rd.mtime) /* already created this file; ignore */
376 break;
377 if(havelocal){
378 if((ld.mode&DMDIR) && (rd.mode&DMDIR))
379 break;
380 if(!ismatch(name)){
381 skip = prstopped(skip, name);
382 continue;
383 }
384 SET(checkedmatch2);
385 if(samecontents(local, remote) > 0){
386 chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
387 goto DoCreateDb;
388 }
389 if(dbd.mtime==ld.mtime && dbd.length==ld.length)
390 goto DoCreate;
391 if(resolve1=='s')
392 goto DoCreate;
393 else if(resolve1 == 'c')
394 goto DoCreateDb;
395 conflict(name, "locally modified; will not overwrite");
396 skip = 1;
397 continue;
398 }
399 if(!ismatch(name)){
400 skip = prstopped(skip, name);
401 continue;
402 }
403 SET(checkedmatch2);
404 DoCreate:
405 USED(checkedmatch2);
406 assert(ismatch(name));
407 if(notexists(remote)){
408 addce(local);
409 /* no skip=1 */
410 break;;
411 }
412 chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
413 if(donothing)
414 break;
415 if(rd.mode&DMDIR){
416 fd = create(local, OREAD, DMDIR);
417 if(fd < 0 && isdir(local))
418 fd = open(local, OREAD);
419 if(fd < 0){
420 error("mkdir %q: %r", name);
421 skip = 1;
422 continue;
423 }
424 nulldir(&nd);
425 nd.mode = rd.mode;
426 if(dirfwstat(fd, &nd) < 0)
427 fprint(2, "warning: cannot set mode on %q\n", local);
428 nulldir(&nd);
429 nd.gid = rd.gid;
430 if(dirfwstat(fd, &nd) < 0)
431 fprint(2, "warning: cannot set gid on %q\n", local);
432 if(douid){
433 nulldir(&nd);
434 nd.uid = rd.uid;
435 if(dirfwstat(fd, &nd) < 0)
436 fprint(2, "warning: cannot set uid on %q\n", local);
437 }
438 close(fd);
439 rd.mtime = now;
440 }else{
441 if(copyfile(local, remote, name, &rd, 1, &k) < 0){
442 if(k)
443 addce(local);
444 skip = 1;
445 continue;
446 }
447 }
448 DoCreateDb:
449 USED(checkedmatch2);
450 assert(ismatch(name));
451 insertdb(clientdb, name, &rd);
452 break;
453
454 case 'c': /* change contents */
455 if(!havedb){
456 if(notexists(remote)){
457 addce(local);
458 /* no skip=1 */
459 break;
460 }
461 if(!ismatch(name)){
462 skip = prstopped(skip, name);
463 continue;
464 }
465 SET(checkedmatch3);
466 if(resolve1 == 's')
467 goto DoCopy;
468 else if(resolve1=='c')
469 goto DoCopyDb;
470 if(samecontents(local, remote) > 0){
471 chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
472 goto DoCopyDb;
473 }
474 if(havelocal)
475 conflict(name, "locally created; will not update");
476 else
477 conflict(name, "not replicated; will not update");
478 skip = 1;
479 continue;
480 }
481 if(dbd.mtime >= rd.mtime) /* already have/had this version; ignore */
482 break;
483 if(!ismatch(name)){
484 skip = prstopped(skip, name);
485 continue;
486 }
487 SET(checkedmatch3);
488 if(!havelocal){
489 if(notexists(remote)){
490 addce(local);
491 /* no skip=1 */
492 break;
493 }
494 if(resolve1 == 's')
495 goto DoCopy;
496 else if(resolve1 == 'c')
497 break;
498 conflict(name, "locally removed; will not update");
499 skip = 1;
500 continue;
501 }
502 assert(havedb && havelocal);
503 if(dbd.mtime != ld.mtime || dbd.length != ld.length){
504 if(notexists(remote)){
505 addce(local);
506 /* no skip=1 */
507 break;
508 }
509 if(samecontents(local, remote) > 0){
510 chat("= %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
511 goto DoCopyDb;
512 }
513 if(resolve1 == 's')
514 goto DoCopy;
515 else if(resolve1 == 'c')
516 goto DoCopyDb;
517 conflict(name, "locally modified; will not update [%llud %lud -> %llud %lud]", dbd.length, dbd.mtime, ld.length, ld.mtime);
518 skip = 1;
519 continue;
520 }
521 DoCopy:
522 USED(checkedmatch3);
523 assert(ismatch(name));
524 if(notexists(remote)){
525 addce(local);
526 /* no skip=1 */
527 break;
528 }
529 chat("c %q\n", name);
530 if(donothing)
531 break;
532 if(copyfile(local, remote, name, &rd, 0, &k) < 0){
533 if(k)
534 addce(local);
535 skip = 1;
536 continue;
537 }
538 DoCopyDb:
539 USED(checkedmatch3);
540 assert(ismatch(name));
541 if(!havedb){
542 if(havelocal)
543 dbd = ld;
544 else
545 dbd = rd;
546 }
547 dbd.mtime = rd.mtime;
548 dbd.length = rd.length;
549 insertdb(clientdb, name, &dbd);
550 break;
551
552 case 'm': /* change metadata */
553 if(!havedb){
554 if(notexists(remote)){
555 addce(local);
556 /* no skip=1 */
557 break;
558 }
559 if(!ismatch(name)){
560 skip = prstopped(skip, name);
561 continue;
562 }
563 SET(checkedmatch4);
564 if(resolve1 == 's'){
565 USED(checkedmatch4);
566 SET(checkedmatch2);
567 goto DoCreate;
568 }
569 else if(resolve1 == 'c')
570 goto DoMetaDb;
571 if(havelocal)
572 conflict(name, "locally created; will not update metadata");
573 else
574 conflict(name, "not replicated; will not update metadata");
575 skip = 1;
576 continue;
577 }
578 if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime) /* have newer version; ignore */
579 break;
580 if((dbd.mode&DMDIR) && dbd.mtime > now)
581 break;
582 if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode)
583 break;
584 if(!havelocal){
585 if(notexists(remote)){
586 addce(local);
587 /* no skip=1 */
588 break;
589 }
590 if(!ismatch(name)){
591 skip = prstopped(skip, name);
592 continue;
593 }
594 SET(checkedmatch4);
595 if(resolve1 == 's'){
596 USED(checkedmatch4);
597 SET(checkedmatch2);
598 goto DoCreate;
599 }
600 else if(resolve1 == 'c')
601 break;
602 conflict(name, "locally removed; will not update metadata");
603 skip = 1;
604 continue;
605 }
606 if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* this check might be overkill */
607 if(notexists(remote)){
608 addce(local);
609 /* no skip=1 */
610 break;
611 }
612 if(!ismatch(name)){
613 skip = prstopped(skip, name);
614 continue;
615 }
616 SET(checkedmatch4);
617 if(resolve1 == 's' || samecontents(local, remote) > 0)
618 goto DoMeta;
619 else if(resolve1 == 'c')
620 break;
621 conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo",
622 dbd.mtime != ld.mtime ? "mtime" :
623 dbd.length != ld.length ? "length" :
624 "unknown",
625 rd.uid, rd.gid, rd.mode);
626 skip = 1;
627 continue;
628 }
629 if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){
630 if(notexists(remote)){
631 addce(local);
632 /* no skip=1 */
633 break;
634 }
635 if(!ismatch(name)){
636 skip = prstopped(skip, name);
637 continue;
638 }
639 SET(checkedmatch4);
640 if(resolve1 == 's')
641 goto DoMeta;
642 else if(resolve1 == 'c')
643 break;
644 conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode);
645 skip = 1;
646 continue;
647 }
648 if(!ismatch(name)){
649 skip = prstopped(skip, name);
650 continue;
651 }
652 SET(checkedmatch4);
653 DoMeta:
654 USED(checkedmatch4);
655 assert(ismatch(name));
656 if(notexists(remote)){
657 addce(local);
658 /* no skip=1 */
659 break;
660 }
661 chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime);
662 if(donothing)
663 break;
664 nulldir(&nd);
665 nd.gid = rd.gid;
666 nd.mode = rd.mode;
667 if(douid)
668 nd.uid = rd.uid;
669 if(dirwstat(local, &nd) < 0){
670 error("dirwstat %q: %r", name);
671 skip = 1;
672 continue;
673 }
674 DoMetaDb:
675 USED(checkedmatch4);
676 assert(ismatch(name));
677 if(!havedb){
678 if(havelocal)
679 dbd = ld;
680 else
681 dbd = rd;
682 }
683 if(dbd.mode&DMDIR)
684 dbd.mtime = now;
685 dbd.gid = rd.gid;
686 dbd.mode = rd.mode;
687 if(douid)
688 dbd.uid = rd.uid;
689 insertdb(clientdb, name, &dbd);
690 break;
691 }
692 if(!skip && !donothing){
693 maxnow = now;
694 maxn = n;
695 }
696 }
697
698 w = avlwalk(copyerr->avl);
699 while(e = (Entry*)avlnext(w))
700 error("copying %q: %s\n", e->name, e->d.name);
701
702 if(timefile)
703 writetimefile();
704 if(nconf)
705 exits("conflicts");
706
707 if(errors)
708 exits("errors");
709 exits(nil);
710 }
711
712
713 char*
mkname(char * buf,int nbuf,char * a,char * b)714 mkname(char *buf, int nbuf, char *a, char *b)
715 {
716 if(strlen(a)+strlen(b)+2 > nbuf)
717 sysfatal("name too long");
718
719 strcpy(buf, a);
720 if(a[strlen(a)-1] != '/')
721 strcat(buf, "/");
722 strcat(buf, b);
723 return buf;
724 }
725
726 int
isdir(char * s)727 isdir(char *s)
728 {
729 ulong m;
730 Dir *d;
731
732 if((d = dirstat(s)) == nil)
733 return 0;
734 m = d->mode;
735 free(d);
736 return (m&DMDIR) != 0;
737 }
738
739 void
conflict(char * name,char * f,...)740 conflict(char *name, char *f, ...)
741 {
742 char *s;
743 va_list arg;
744
745 va_start(arg, f);
746 s = vsmprint(f, arg);
747 va_end(arg);
748
749 fprint(2, "! %s: %s\n", name, s);
750 free(s);
751
752 nconf++;
753 }
754
755 void
error(char * f,...)756 error(char *f, ...)
757 {
758 char *s;
759 va_list arg;
760
761 va_start(arg, f);
762 s = vsmprint(f, arg);
763 va_end(arg);
764 fprint(2, "error: %s\n", s);
765 free(s);
766 errors = 1;
767 }
768
769 int
ismatch(char * s)770 ismatch(char *s)
771 {
772 int i, len;
773
774 if(nmatch == 0)
775 return 1;
776 for(i=0; i<nmatch; i++){
777 len = strlen(match[i]);
778 if(len == 0)
779 return 1;
780 if(strncmp(s, match[i], len) == 0 && (s[len]=='/' || s[len] == 0))
781 return 1;
782 }
783 return 0;
784 }
785
786 int
localdirstat(char * name,Dir * d)787 localdirstat(char *name, Dir *d)
788 {
789 static Dir *d2;
790
791 free(d2);
792 if((d2 = dirstat(name)) == nil)
793 return -1;
794 *d = *d2;
795 return 0;
796 }
797
798 enum { DEFB = 8192 };
799
800 static int
cmp1(int fd1,int fd2)801 cmp1(int fd1, int fd2)
802 {
803 char buf1[DEFB];
804 char buf2[DEFB];
805 int n1, n2;
806
807 for(;;){
808 n1 = readn(fd1, buf1, DEFB);
809 n2 = readn(fd2, buf2, DEFB);
810 if(n1 < 0 || n2 < 0)
811 return -1;
812 if(n1 != n2)
813 return 0;
814 if(n1 == 0)
815 return 1;
816 if(memcmp(buf1, buf2, n1) != 0)
817 return 0;
818 }
819 }
820
821 static int
copy1(int fdf,int fdt,char * from,char * to)822 copy1(int fdf, int fdt, char *from, char *to)
823 {
824 int i, n, rv, pid[Nwork];
825 Waitmsg *w;
826
827 n = 0;
828 off = 0;
829 for(i=0; i<Nwork; i++){
830 switch(pid[n] = rfork(RFPROC|RFMEM)){
831 case 0:
832 notify(failure);
833 worker(fdf, fdt, from, to);
834 case -1:
835 break;
836 default:
837 n++;
838 break;
839 }
840 }
841 if(n == 0){
842 fprint(2, "cp: rfork: %r\n");
843 return -1;
844 }
845
846 rv = 0;
847 while((w = wait()) != nil){
848 if(w->msg[0]){
849 rv = -1;
850 for(i=0; i<n; i++)
851 if(pid[i] > 0)
852 postnote(PNPROC, pid[i], "failure");
853 }
854 free(w);
855 }
856 return rv;
857 }
858
859 void
worker(int fdf,int fdt,char * from,char * to)860 worker(int fdf, int fdt, char *from, char *to)
861 {
862 char buf[DEFB], *bp;
863 long len, n;
864 vlong o;
865
866 len = sizeof(buf);
867 bp = buf;
868 o = nextoff();
869
870 while(n = pread(fdf, bp, len, o)){
871 if(n < 0){
872 fprint(2, "reading %s: %r\n", from);
873 _exits("bad");
874 }
875 if(pwrite(fdt, buf, n, o) != n){
876 fprint(2, "writing %s: %r\n", to);
877 _exits("bad");
878 }
879 bp += n;
880 o += n;
881 len -= n;
882 if(len == 0){
883 len = sizeof buf;
884 bp = buf;
885 o = nextoff();
886 }
887 }
888 _exits(nil);
889 }
890
891 vlong
nextoff(void)892 nextoff(void)
893 {
894 vlong o;
895
896 qlock(&lk);
897 o = off;
898 off += DEFB;
899 qunlock(&lk);
900
901 return o;
902 }
903
904 void
failure(void *,char * note)905 failure(void*, char *note)
906 {
907 if(strcmp(note, "failure") == 0)
908 _exits(nil);
909 noted(NDFLT);
910 }
911
912
913 static int
opentemp(char * template)914 opentemp(char *template)
915 {
916 int fd, i;
917 char *p;
918
919 p = estrdup(template);
920 fd = -1;
921 for(i=0; i<10; i++){
922 mktemp(p);
923 if((fd=create(p, ORDWR|OEXCL|ORCLOSE, 0000)) >= 0)
924 break;
925 strcpy(p, template);
926 }
927 if(fd < 0)
928 return -1;
929 strcpy(template, p);
930 free(p);
931 return fd;
932 }
933
934 static int
copytotemp(char * remote,int rfd,Dir * d0)935 copytotemp(char *remote, int rfd, Dir *d0)
936 {
937 int tfd;
938 char tmp[32];
939 Dir *d1;
940
941 strcpy(tmp, "/tmp/replicaXXXXXXXX");
942 tfd = opentemp(tmp);
943 if(tfd < 0)
944 return -1;
945 if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){
946 close(tfd);
947 return -1;
948 }
949
950 if(d0->qid.path != d1->qid.path
951 || d0->qid.vers != d1->qid.vers
952 || d0->mtime != d1->mtime
953 || d0->length != d1->length){
954 /* file changed underfoot; go around again */
955 free(d1);
956 close(tfd);
957 return -2;
958 }
959 free(d1);
960 return tfd;
961 }
962
963 int
copyfile(char * local,char * remote,char * name,Dir * d,int dowstat,int * printerror)964 copyfile(char *local, char *remote, char *name, Dir *d, int dowstat,
965 int *printerror)
966 {
967 Dir *d0, *dl;
968 Dir nd;
969 int rfd, tfd, wfd, didcreate;
970 char tmp[32], *p, *safe;
971 char err[ERRMAX];
972
973 do {
974 *printerror = 0;
975 if((rfd = open(remote, OREAD)) < 0)
976 return -1;
977
978 d0 = dirfstat(rfd);
979 if(d0 == nil){
980 close(rfd);
981 return -1;
982 }
983 *printerror = 1;
984 if(!tempspool){
985 tfd = rfd;
986 goto DoCopy;
987 }
988
989 tfd = copytotemp(remote, rfd, d0);
990 close(rfd);
991 if (tfd < 0) {
992 free(d0);
993 if (tfd == -1)
994 return -1;
995 }
996 } while(tfd == -2);
997 if(seek(tfd, 0, 0) != 0){
998 close(tfd);
999 free(d0);
1000 return -1;
1001 }
1002
1003 DoCopy:
1004 /*
1005 * clumsy but important hack to do safeinstall-like installs.
1006 */
1007 p = strchr(name, '/');
1008 if(safeinstall && p && strncmp(p, "/bin/", 5) == 0 && access(local, AEXIST) >= 0){
1009 /*
1010 * remove bin/_targ
1011 */
1012 safe = emalloc(strlen(local)+2);
1013 strcpy(safe, local);
1014 p = strrchr(safe, '/')+1;
1015 memmove(p+1, p, strlen(p)+1);
1016 p[0] = '_';
1017 remove(safe); /* ignore failure */
1018
1019 /*
1020 * rename bin/targ to bin/_targ
1021 */
1022 nulldir(&nd);
1023 nd.name = p;
1024 if(dirwstat(local, &nd) < 0)
1025 fprint(2, "warning: rename %s to %s: %r\n", local, p);
1026 }
1027
1028 didcreate = 0;
1029 if((dl = dirstat(local)) == nil){
1030 if((wfd = create(local, OWRITE, 0)) >= 0){
1031 didcreate = 1;
1032 goto okay;
1033 }
1034 goto err;
1035 }else{
1036 if((wfd = open(local, OTRUNC|OWRITE)) >= 0)
1037 goto okay;
1038 rerrstr(err, sizeof err);
1039 if(strstr(err, "permission") == nil)
1040 goto err;
1041 nulldir(&nd);
1042 /*
1043 * Assume the person running pull is in the appropriate
1044 * groups. We could set 0666 instead, but I'm worried
1045 * about leaving the file world-readable or world-writable
1046 * when it shouldn't be.
1047 */
1048 nd.mode = dl->mode | 0660;
1049 if(nd.mode == dl->mode)
1050 goto err;
1051 if(dirwstat(local, &nd) < 0)
1052 goto err;
1053 if((wfd = open(local, OTRUNC|OWRITE)) >= 0){
1054 nd.mode = dl->mode;
1055 if(dirfwstat(wfd, &nd) < 0)
1056 fprint(2, "warning: set mode on %s to 0660 to open; cannot set back to %luo: %r\n", local, nd.mode);
1057 goto okay;
1058 }
1059 nd.mode = dl->mode;
1060 if(dirwstat(local, &nd) < 0)
1061 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);
1062 goto err;
1063 }
1064
1065 err:
1066 close(tfd);
1067 free(d0);
1068 free(dl);
1069 return -1;
1070
1071 okay:
1072 free(dl);
1073 if(copy1(tfd, wfd, tmp, local) < 0){
1074 close(tfd);
1075 close(wfd);
1076 free(d0);
1077 return -1;
1078 }
1079 close(tfd);
1080 if(didcreate || dowstat){
1081 nulldir(&nd);
1082 nd.mode = d->mode;
1083 if(dirfwstat(wfd, &nd) < 0)
1084 fprint(2, "warning: cannot set mode on %s\n", local);
1085 nulldir(&nd);
1086 nd.gid = d->gid;
1087 if(dirfwstat(wfd, &nd) < 0)
1088 fprint(2, "warning: cannot set gid on %s\n", local);
1089 if(douid){
1090 nulldir(&nd);
1091 nd.uid = d->uid;
1092 if(dirfwstat(wfd, &nd) < 0)
1093 fprint(2, "warning: cannot set uid on %s\n", local);
1094 }
1095 }
1096 d->mtime = d0->mtime;
1097 d->length = d0->length;
1098 nulldir(&nd);
1099 nd.mtime = d->mtime;
1100 if(dirfwstat(wfd, &nd) < 0)
1101 fprint(2, "warning: cannot set mtime on %s\n", local);
1102 free(d0);
1103
1104 close(wfd);
1105 return 0;
1106 }
1107
1108 int
samecontents(char * local,char * remote)1109 samecontents(char *local, char *remote)
1110 {
1111 Dir *d0, *d1;
1112 int rfd, tfd, lfd, ret;
1113
1114 /* quick check: sizes must match */
1115 d1 = nil;
1116 if((d0 = dirstat(local)) == nil || (d1 = dirstat(remote)) == nil){
1117 free(d0);
1118 free(d1);
1119 return -1;
1120 }
1121 if(d0->length != d1->length){
1122 free(d0);
1123 free(d1);
1124 return 0;
1125 }
1126
1127 do {
1128 if((rfd = open(remote, OREAD)) < 0)
1129 return -1;
1130 d0 = dirfstat(rfd);
1131 if(d0 == nil){
1132 close(rfd);
1133 return -1;
1134 }
1135
1136 tfd = copytotemp(remote, rfd, d0);
1137 close(rfd);
1138 free(d0);
1139 if (tfd == -1)
1140 return -1;
1141 } while(tfd == -2);
1142 if(seek(tfd, 0, 0) != 0){
1143 close(tfd);
1144 return -1;
1145 }
1146
1147 /*
1148 * now compare
1149 */
1150 if((lfd = open(local, OREAD)) < 0){
1151 close(tfd);
1152 return -1;
1153 }
1154
1155 ret = cmp1(lfd, tfd);
1156 close(lfd);
1157 close(tfd);
1158 return ret;
1159 }
1160
1161 /*
1162 * Applylog might try to overwrite itself.
1163 * To avoid problems with this, we copy ourselves
1164 * into /tmp and then re-exec.
1165 */
1166 char *rmargv0;
1167
1168 static void
rmself(void)1169 rmself(void)
1170 {
1171 remove(rmargv0);
1172 }
1173
1174 static int
genopentemp(char * template,int mode,int perm)1175 genopentemp(char *template, int mode, int perm)
1176 {
1177 int fd, i;
1178 char *p;
1179
1180 p = estrdup(template);
1181 fd = -1;
1182 for(i=0; i<10; i++){
1183 mktemp(p);
1184 if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
1185 break;
1186 strcpy(p, template);
1187 }
1188 if(fd < 0)
1189 sysfatal("could not create temporary file");
1190
1191 strcpy(template, p);
1192 free(p);
1193
1194 return fd;
1195 }
1196
1197 static void
membogus(char ** argv)1198 membogus(char **argv)
1199 {
1200 int n, fd, wfd;
1201 char template[50], buf[1024];
1202
1203 if(strncmp(argv[0], "/tmp/_applylog_", 1+3+1+1+8+1)==0) {
1204 rmargv0 = argv[0];
1205 atexit(rmself);
1206 return;
1207 }
1208
1209 if((fd = open(argv[0], OREAD)) < 0)
1210 return;
1211
1212 strcpy(template, "/tmp/_applylog_XXXXXX");
1213 if((wfd = genopentemp(template, OWRITE, 0700)) < 0)
1214 return;
1215
1216 while((n = read(fd, buf, sizeof buf)) > 0)
1217 if(write(wfd, buf, n) != n)
1218 goto Error;
1219
1220 if(n != 0)
1221 goto Error;
1222
1223 close(fd);
1224 close(wfd);
1225
1226 argv[0] = template;
1227 exec(template, argv);
1228 fprint(2, "exec error %r\n");
1229
1230 Error:
1231 close(fd);
1232 close(wfd);
1233 remove(template);
1234 return;
1235 }
1236