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