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