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 tempspool = 1; 26 int safeinstall = 1; 27 char *lroot; 28 char *rroot; 29 Db *clientdb; 30 int skip; 31 int douid; 32 char *mkname(char*, int, char*, char*); 33 char localbuf[10240]; 34 char remotebuf[10240]; 35 int copyfile(char*, char*, char*, Dir*, int, int*); 36 ulong maxnow; 37 int maxn; 38 char *timefile; 39 int timefd; 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(!ismatch(name)){ 292 if(!skip) 293 fprint(2, "stopped updating log apply time because of %s\n", name); 294 skip = 1; 295 continue; 296 } 297 SET(checkedmatch1); 298 if(!havedb){ 299 if(resolve1 == 's') 300 goto DoRemove; 301 else if(resolve1 == 'c') 302 goto DoRemoveDb; 303 conflict(name, "locally created; will not remove"); 304 skip = 1; 305 continue; 306 } 307 assert(havelocal && havedb); 308 if(dbd.mtime > rd.mtime) /* we have a newer file than what was deleted */ 309 break; 310 if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* locally modified since we downloaded it */ 311 if(resolve1 == 's') 312 goto DoRemove; 313 else if(resolve1 == 'c') 314 break; 315 conflict(name, "locally modified; will not remove"); 316 skip = 1; 317 continue; 318 } 319 DoRemove: 320 USED(checkedmatch1); 321 assert(ismatch(name)); 322 chat("d %q\n", name); 323 if(donothing) 324 break; 325 if(remove(local) < 0){ 326 error("removing %q", name); 327 skip = 1; 328 continue; 329 } 330 DoRemoveDb: 331 USED(checkedmatch1); 332 assert(ismatch(name)); 333 removedb(clientdb, name); 334 break; 335 336 case 'a': /* add file */ 337 if(!havedb){ 338 if(!ismatch(name)){ 339 if(!skip) 340 fprint(2, "stopped updating log apply time because of %s\n", name); 341 skip = 1; 342 continue; 343 } 344 SET(checkedmatch2); 345 if(!havelocal) 346 goto DoCreate; 347 if((ld.mode&DMDIR) && (rd.mode&DMDIR)) 348 break; 349 if(resolve1 == 's') 350 goto DoCreate; 351 else if(resolve1 == 'c') 352 goto DoCreateDb; 353 conflict(name, "locally created; will not overwrite"); 354 skip = 1; 355 continue; 356 } 357 assert(havedb); 358 if(dbd.mtime >= rd.mtime) /* already created this file; ignore */ 359 break; 360 if(havelocal){ 361 if((ld.mode&DMDIR) && (rd.mode&DMDIR)) 362 break; 363 if(!ismatch(name)){ 364 if(!skip) 365 fprint(2, "stopped updating log apply time because of %s\n", name); 366 skip = 1; 367 continue; 368 } 369 SET(checkedmatch2); 370 if(dbd.mtime==ld.mtime && dbd.length==ld.length) 371 goto DoCreate; 372 if(resolve1=='s') 373 goto DoCreate; 374 else if(resolve1 == 'c') 375 break; 376 conflict(name, "locally modified; will not overwrite"); 377 skip = 1; 378 continue; 379 } 380 if(!ismatch(name)){ 381 if(!skip) 382 fprint(2, "stopped updating log apply time because of %s\n", name); 383 skip = 1; 384 continue; 385 } 386 SET(checkedmatch2); 387 DoCreate: 388 USED(checkedmatch2); 389 assert(ismatch(name)); 390 if(notexists(remote)){ 391 addce(local); 392 /* no skip=1 */ 393 break;; 394 } 395 chat("a %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime); 396 if(donothing) 397 break; 398 if(rd.mode&DMDIR){ 399 if((fd = create(local, OREAD, DMDIR)) < 0){ 400 error("mkdir %q: %r", name); 401 skip = 1; 402 continue; 403 } 404 nulldir(&nd); 405 nd.mode = rd.mode; 406 if(dirfwstat(fd, &nd) < 0) 407 fprint(2, "warning: cannot set mode on %q\n", local); 408 nulldir(&nd); 409 nd.gid = rd.gid; 410 if(dirfwstat(fd, &nd) < 0) 411 fprint(2, "warning: cannot set gid on %q\n", local); 412 if(douid){ 413 nulldir(&nd); 414 nd.uid = rd.uid; 415 if(dirfwstat(fd, &nd) < 0) 416 fprint(2, "warning: cannot set uid on %q\n", local); 417 } 418 close(fd); 419 rd.mtime = now; 420 }else{ 421 if(copyfile(local, remote, name, &rd, 1, &k) < 0){ 422 if(k) 423 addce(local); 424 skip = 1; 425 continue; 426 } 427 } 428 DoCreateDb: 429 USED(checkedmatch2); 430 assert(ismatch(name)); 431 insertdb(clientdb, name, &rd); 432 break; 433 434 case 'c': /* change contents */ 435 if(!havedb){ 436 if(notexists(remote)){ 437 addce(local); 438 /* no skip=1 */ 439 break; 440 } 441 if(!ismatch(name)){ 442 if(!skip) 443 fprint(2, "stopped updating log apply time because of %s\n", name); 444 skip = 1; 445 continue; 446 } 447 SET(checkedmatch3); 448 if(resolve1 == 's') 449 goto DoCopy; 450 else if(resolve1=='c') 451 goto DoCopyDb; 452 if(havelocal) 453 conflict(name, "locally created; will not update"); 454 else 455 conflict(name, "not replicated; will not update"); 456 skip = 1; 457 continue; 458 } 459 if(dbd.mtime >= rd.mtime) /* already have/had this version; ignore */ 460 break; 461 if(!ismatch(name)){ 462 if(!skip) 463 fprint(2, "stopped updating log apply time because of %s\n", name); 464 skip = 1; 465 continue; 466 } 467 SET(checkedmatch3); 468 if(!havelocal){ 469 if(notexists(remote)){ 470 addce(local); 471 /* no skip=1 */ 472 break; 473 } 474 if(resolve1 == 's') 475 goto DoCopy; 476 else if(resolve1 == 'c') 477 break; 478 conflict(name, "locally removed; will not update"); 479 skip = 1; 480 continue; 481 } 482 assert(havedb && havelocal); 483 if(dbd.mtime != ld.mtime || dbd.length != ld.length){ 484 if(notexists(remote)){ 485 addce(local); 486 /* no skip=1 */ 487 break; 488 } 489 if(resolve1 == 's') 490 goto DoCopy; 491 else if(resolve1 == 'c') 492 break; 493 conflict(name, "locally modified; will not update"); 494 skip = 1; 495 continue; 496 } 497 DoCopy: 498 USED(checkedmatch3); 499 assert(ismatch(name)); 500 if(notexists(remote)){ 501 addce(local); 502 /* no skip=1 */ 503 break; 504 } 505 chat("c %q\n", name); 506 if(donothing) 507 break; 508 if(copyfile(local, remote, name, &rd, 0, &k) < 0){ 509 if(k) 510 addce(local); 511 skip = 1; 512 continue; 513 } 514 DoCopyDb: 515 USED(checkedmatch3); 516 assert(ismatch(name)); 517 if(!havedb){ 518 if(havelocal) 519 dbd = ld; 520 else 521 dbd = rd; 522 } 523 dbd.mtime = rd.mtime; 524 dbd.length = rd.length; 525 insertdb(clientdb, name, &dbd); 526 break; 527 528 case 'm': /* change metadata */ 529 if(!havedb){ 530 if(notexists(remote)){ 531 addce(local); 532 /* no skip=1 */ 533 break; 534 } 535 if(!ismatch(name)){ 536 if(!skip) 537 fprint(2, "stopped updating log apply time because of %s\n", name); 538 skip = 1; 539 continue; 540 } 541 SET(checkedmatch4); 542 if(resolve1 == 's'){ 543 USED(checkedmatch4); 544 SET(checkedmatch2); 545 goto DoCreate; 546 } 547 else if(resolve1 == 'c') 548 goto DoMetaDb; 549 if(havelocal) 550 conflict(name, "locally created; will not update metadata"); 551 else 552 conflict(name, "not replicated; will not update metadata"); 553 skip = 1; 554 continue; 555 } 556 if(!(dbd.mode&DMDIR) && dbd.mtime > rd.mtime) /* have newer version; ignore */ 557 break; 558 if((dbd.mode&DMDIR) && dbd.mtime > now) 559 break; 560 if(havelocal && (!douid || strcmp(ld.uid, rd.uid)==0) && strcmp(ld.gid, rd.gid)==0 && ld.mode==rd.mode) 561 break; 562 if(!havelocal){ 563 if(notexists(remote)){ 564 addce(local); 565 /* no skip=1 */ 566 break; 567 } 568 if(!ismatch(name)){ 569 if(!skip) 570 fprint(2, "stopped updating log apply time because of %s\n", name); 571 skip = 1; 572 continue; 573 } 574 SET(checkedmatch4); 575 if(resolve1 == 's'){ 576 USED(checkedmatch4); 577 SET(checkedmatch2); 578 goto DoCreate; 579 } 580 else if(resolve1 == 'c') 581 break; 582 conflict(name, "locally removed; will not update metadata"); 583 skip = 1; 584 continue; 585 } 586 if(!(dbd.mode&DMDIR) && (dbd.mtime != ld.mtime || dbd.length != ld.length)){ /* this check might be overkill */ 587 if(notexists(remote)){ 588 addce(local); 589 /* no skip=1 */ 590 break; 591 } 592 if(!ismatch(name)){ 593 if(!skip) 594 fprint(2, "stopped updating log apply time because of %s\n", name); 595 skip = 1; 596 continue; 597 } 598 SET(checkedmatch4); 599 if(resolve1 == 's') 600 goto DoMeta; 601 else if(resolve1 == 'c') 602 break; 603 conflict(name, "contents locally modified (%s); will not update metadata to %s %s %luo", 604 dbd.mtime != ld.mtime ? "mtime" : 605 dbd.length != ld.length ? "length" : 606 "unknown", 607 rd.uid, rd.gid, rd.mode); 608 skip = 1; 609 continue; 610 } 611 if((douid && strcmp(ld.uid, dbd.uid)!=0) || strcmp(ld.gid, dbd.gid)!=0 || ld.mode!=dbd.mode){ 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') 625 goto DoMeta; 626 else if(resolve1 == 'c') 627 break; 628 conflict(name, "metadata locally changed; will not update metadata to %s %s %luo", rd.uid, rd.gid, rd.mode); 629 skip = 1; 630 continue; 631 } 632 if(!ismatch(name)){ 633 if(!skip) 634 fprint(2, "stopped updating log apply time because of %s\n", name); 635 skip = 1; 636 continue; 637 } 638 SET(checkedmatch4); 639 DoMeta: 640 USED(checkedmatch4); 641 assert(ismatch(name)); 642 if(notexists(remote)){ 643 addce(local); 644 /* no skip=1 */ 645 break; 646 } 647 chat("m %q %luo %q %q %lud\n", name, rd.mode, rd.uid, rd.gid, rd.mtime); 648 if(donothing) 649 break; 650 nulldir(&nd); 651 nd.gid = rd.gid; 652 nd.mode = rd.mode; 653 if(douid) 654 nd.uid = rd.uid; 655 if(dirwstat(local, &nd) < 0){ 656 error("dirwstat %q: %r", name); 657 skip = 1; 658 continue; 659 } 660 DoMetaDb: 661 USED(checkedmatch4); 662 assert(ismatch(name)); 663 if(!havedb){ 664 if(havelocal) 665 dbd = ld; 666 else 667 dbd = rd; 668 } 669 if(dbd.mode&DMDIR) 670 dbd.mtime = now; 671 dbd.gid = rd.gid; 672 dbd.mode = rd.mode; 673 if(douid) 674 dbd.uid = rd.uid; 675 insertdb(clientdb, name, &dbd); 676 break; 677 } 678 if(!skip && !donothing){ 679 maxnow = now; 680 maxn = n; 681 } 682 } 683 684 w = avlwalk(copyerr->avl); 685 while(e = (Entry*)avlnext(w)) 686 error("copying %q: %s\n", e->name, e->d.name); 687 688 if(timefile) 689 writetimefile(); 690 if(nconf) 691 exits("conflicts"); 692 693 if(errors) 694 exits("errors"); 695 exits(nil); 696 } 697 698 699 char* 700 mkname(char *buf, int nbuf, char *a, char *b) 701 { 702 if(strlen(a)+strlen(b)+2 > nbuf) 703 sysfatal("name too long"); 704 705 strcpy(buf, a); 706 if(a[strlen(a)-1] != '/') 707 strcat(buf, "/"); 708 strcat(buf, b); 709 return buf; 710 } 711 712 int 713 isdir(char *s) 714 { 715 ulong m; 716 Dir *d; 717 718 if((d = dirstat(s)) == nil) 719 return 0; 720 m = d->mode; 721 free(d); 722 return (m&DMDIR) != 0; 723 } 724 725 void 726 conflict(char *name, char *f, ...) 727 { 728 char *s; 729 va_list arg; 730 731 va_start(arg, f); 732 s = vsmprint(f, arg); 733 va_end(arg); 734 735 fprint(2, "%s: %s\n", name, s); 736 free(s); 737 738 nconf++; 739 // if(nconf%16 == 0) 740 // conf = erealloc(conf, (nconf+16)*sizeof(conf[0])); 741 // conf[nconf++] = estrdup(name); 742 } 743 744 void 745 error(char *f, ...) 746 { 747 char *s; 748 va_list arg; 749 750 va_start(arg, f); 751 s = vsmprint(f, arg); 752 va_end(arg); 753 fprint(2, "error: %s\n", s); 754 free(s); 755 errors = 1; 756 } 757 758 int 759 ismatch(char *s) 760 { 761 int i, len; 762 763 if(nmatch == 0) 764 return 1; 765 for(i=0; i<nmatch; i++){ 766 len = strlen(match[i]); 767 if(len == 0) 768 return 1; 769 if(strncmp(s, match[i], len) == 0 && (s[len]=='/' || s[len] == 0)) 770 return 1; 771 } 772 return 0; 773 } 774 775 int 776 localdirstat(char *name, Dir *d) 777 { 778 static Dir *d2; 779 780 free(d2); 781 if((d2 = dirstat(name)) == nil) 782 return -1; 783 *d = *d2; 784 return 0; 785 } 786 787 enum { DEFB = 8192 }; 788 789 static int 790 copy1(int fdf, int fdt, char *from, char *to) 791 { 792 int i, n, rv, pid[Nwork]; 793 Waitmsg *w; 794 795 n = 0; 796 off = 0; 797 for(i=0; i<Nwork; i++){ 798 switch(pid[n] = rfork(RFPROC|RFMEM)){ 799 case 0: 800 notify(failure); 801 worker(fdf, fdt, from, to); 802 case -1: 803 break; 804 default: 805 n++; 806 break; 807 } 808 } 809 if(n == 0){ 810 fprint(2, "cp: rfork: %r\n"); 811 return -1; 812 } 813 814 rv = 0; 815 while((w = wait()) != nil){ 816 if(w->msg[0]){ 817 rv = -1; 818 for(i=0; i<n; i++) 819 if(pid[i] > 0) 820 postnote(PNPROC, pid[i], "failure"); 821 } 822 free(w); 823 } 824 return rv; 825 } 826 827 void 828 worker(int fdf, int fdt, char *from, char *to) 829 { 830 char buf[DEFB], *bp; 831 long len, n; 832 vlong o; 833 834 len = sizeof(buf); 835 bp = buf; 836 o = nextoff(); 837 838 while(n = pread(fdf, bp, len, o)){ 839 if(n < 0){ 840 fprint(2, "reading %s: %r\n", from); 841 _exits("bad"); 842 } 843 if(pwrite(fdt, buf, n, o) != n){ 844 fprint(2, "writing %s: %r\n", to); 845 _exits("bad"); 846 } 847 bp += n; 848 o += n; 849 len -= n; 850 if(len == 0){ 851 len = sizeof buf; 852 bp = buf; 853 o = nextoff(); 854 } 855 } 856 _exits(nil); 857 } 858 859 vlong 860 nextoff(void) 861 { 862 vlong o; 863 864 qlock(&lk); 865 o = off; 866 off += DEFB; 867 qunlock(&lk); 868 869 return o; 870 } 871 872 void 873 failure(void*, char *note) 874 { 875 if(strcmp(note, "failure") == 0) 876 _exits(nil); 877 noted(NDFLT); 878 } 879 880 881 static int 882 opentemp(char *template) 883 { 884 int fd, i; 885 char *p; 886 887 p = estrdup(template); 888 fd = -1; 889 for(i=0; i<10; i++){ 890 mktemp(p); 891 if((fd=create(p, ORDWR|OEXCL|ORCLOSE, 0000)) >= 0) 892 break; 893 strcpy(p, template); 894 } 895 if(fd < 0) 896 return -1; 897 strcpy(template, p); 898 free(p); 899 return fd; 900 } 901 902 int 903 copyfile(char *local, char *remote, char *name, Dir *d, int dowstat, int *printerror) 904 { 905 Dir *d0, *d1, *dl; 906 Dir nd; 907 int rfd, tfd, wfd, didcreate; 908 char tmp[32], *p, *safe; 909 char err[ERRMAX]; 910 911 Again: 912 *printerror = 0; 913 if((rfd = open(remote, OREAD)) < 0) 914 return -1; 915 916 d0 = dirfstat(rfd); 917 if(d0 == nil){ 918 close(rfd); 919 return -1; 920 } 921 *printerror = 1; 922 if(!tempspool){ 923 tfd = rfd; 924 goto DoCopy; 925 } 926 strcpy(tmp, "/tmp/replicaXXXXXXXX"); 927 tfd = opentemp(tmp); 928 if(tfd < 0){ 929 close(rfd); 930 free(d0); 931 return -1; 932 } 933 if(copy1(rfd, tfd, remote, tmp) < 0 || (d1 = dirfstat(rfd)) == nil){ 934 close(rfd); 935 close(tfd); 936 free(d0); 937 return -1; 938 } 939 close(rfd); 940 if(d0->qid.path != d1->qid.path 941 || d0->qid.vers != d1->qid.vers 942 || d0->mtime != d1->mtime 943 || d0->length != d1->length){ 944 /* file changed underfoot; go around again */ 945 close(tfd); 946 free(d0); 947 free(d1); 948 goto Again; 949 } 950 free(d1); 951 if(seek(tfd, 0, 0) != 0){ 952 close(tfd); 953 free(d0); 954 return -1; 955 } 956 957 DoCopy: 958 /* 959 * clumsy but important hack to do safeinstall-like installs. 960 */ 961 p = strchr(name, '/'); 962 if(safeinstall && p && strncmp(p, "/bin/", 5) == 0 && access(local, AEXIST) >= 0){ 963 /* 964 * remove bin/_targ 965 */ 966 safe = emalloc(strlen(local)+2); 967 strcpy(safe, local); 968 p = strrchr(safe, '/')+1; 969 memmove(p+1, p, strlen(p)+1); 970 p[0] = '_'; 971 remove(safe); /* ignore failure */ 972 973 /* 974 * rename bin/targ to bin/_targ 975 */ 976 nulldir(&nd); 977 nd.name = p; 978 if(dirwstat(local, &nd) < 0) 979 fprint(2, "warning: rename %s to %s: %r\n", local, p); 980 } 981 982 didcreate = 0; 983 if((dl = dirstat(local)) == nil){ 984 if((wfd = create(local, OWRITE, 0)) >= 0){ 985 didcreate = 1; 986 goto okay; 987 } 988 goto err; 989 }else{ 990 if((wfd = open(local, OTRUNC|OWRITE)) >= 0) 991 goto okay; 992 rerrstr(err, sizeof err); 993 if(strstr(err, "permission") == nil) 994 goto err; 995 nulldir(&nd); 996 /* 997 * Assume the person running pull is in the appropriate 998 * groups. We could set 0666 instead, but I'm worried 999 * about leaving the file world-readable or world-writable 1000 * when it shouldn't be. 1001 */ 1002 nd.mode = dl->mode | 0660; 1003 if(nd.mode == dl->mode) 1004 goto err; 1005 if(dirwstat(local, &nd) < 0) 1006 goto err; 1007 if((wfd = open(local, OTRUNC|OWRITE)) >= 0){ 1008 nd.mode = dl->mode; 1009 if(dirfwstat(wfd, &nd) < 0) 1010 fprint(2, "warning: set mode on %s to 0660 to open; cannot set back to %luo: %r\n", local, nd.mode); 1011 goto okay; 1012 } 1013 nd.mode = dl->mode; 1014 if(dirwstat(local, &nd) < 0) 1015 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); 1016 goto err; 1017 } 1018 1019 err: 1020 close(tfd); 1021 free(d0); 1022 free(dl); 1023 return -1; 1024 1025 okay: 1026 free(dl); 1027 if(copy1(tfd, wfd, tmp, local) < 0){ 1028 close(tfd); 1029 close(wfd); 1030 free(d0); 1031 return -1; 1032 } 1033 close(tfd); 1034 if(didcreate || dowstat){ 1035 nulldir(&nd); 1036 nd.mode = d->mode; 1037 if(dirfwstat(wfd, &nd) < 0) 1038 fprint(2, "warning: cannot set mode on %s\n", local); 1039 nulldir(&nd); 1040 nd.gid = d->gid; 1041 if(dirfwstat(wfd, &nd) < 0) 1042 fprint(2, "warning: cannot set gid on %s\n", local); 1043 if(douid){ 1044 nulldir(&nd); 1045 nd.uid = d->uid; 1046 if(dirfwstat(wfd, &nd) < 0) 1047 fprint(2, "warning: cannot set uid on %s\n", local); 1048 } 1049 } 1050 d->mtime = d0->mtime; 1051 d->length = d0->length; 1052 nulldir(&nd); 1053 nd.mtime = d->mtime; 1054 if(dirfwstat(wfd, &nd) < 0) 1055 fprint(2, "warning: cannot set mtime on %s\n", local); 1056 free(d0); 1057 1058 close(wfd); 1059 return 0; 1060 } 1061 1062 /* 1063 * Applylog might try to overwrite itself. 1064 * To avoid problems with this, we copy ourselves 1065 * into /tmp and then re-exec. 1066 */ 1067 char *rmargv0; 1068 1069 static void 1070 rmself(void) 1071 { 1072 remove(rmargv0); 1073 } 1074 1075 static int 1076 genopentemp(char *template, int mode, int perm) 1077 { 1078 int fd, i; 1079 char *p; 1080 1081 p = estrdup(template); 1082 fd = -1; 1083 for(i=0; i<10; i++){ 1084 mktemp(p); 1085 if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0) 1086 break; 1087 strcpy(p, template); 1088 } 1089 if(fd < 0) 1090 sysfatal("could not create temporary file"); 1091 1092 strcpy(template, p); 1093 free(p); 1094 1095 return fd; 1096 } 1097 1098 static void 1099 membogus(char **argv) 1100 { 1101 int n, fd, wfd; 1102 char template[50], buf[1024]; 1103 1104 if(strncmp(argv[0], "/tmp/_applylog_", 1+3+1+1+8+1)==0) { 1105 rmargv0 = argv[0]; 1106 atexit(rmself); 1107 return; 1108 } 1109 1110 if((fd = open(argv[0], OREAD)) < 0) 1111 return; 1112 1113 strcpy(template, "/tmp/_applylog_XXXXXX"); 1114 if((wfd = genopentemp(template, OWRITE, 0700)) < 0) 1115 return; 1116 1117 while((n = read(fd, buf, sizeof buf)) > 0) 1118 if(write(wfd, buf, n) != n) 1119 goto Error; 1120 1121 if(n != 0) 1122 goto Error; 1123 1124 close(fd); 1125 close(wfd); 1126 1127 argv[0] = template; 1128 exec(template, argv); 1129 fprint(2, "exec error %r\n"); 1130 1131 Error: 1132 close(fd); 1133 close(wfd); 1134 remove(template); 1135 return; 1136 } 1137