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 int 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 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* 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 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 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 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 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 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 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 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 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 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 905 failure(void*, char *note) 906 { 907 if(strcmp(note, "failure") == 0) 908 _exits(nil); 909 noted(NDFLT); 910 } 911 912 913 static int 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 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 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 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 1169 rmself(void) 1170 { 1171 remove(rmargv0); 1172 } 1173 1174 static int 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 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