1 /*- 2 * CPDUP.C 3 * 4 * CPDUP <options> source destination 5 * 6 * (c) Copyright 1997-1999 by Matthew Dillon and Dima Ruban. Permission to 7 * use and distribute based on the FreeBSD copyright. Supplied as-is, 8 * USE WITH EXTREME CAUTION. 9 * 10 * This program attempts to duplicate the source onto the destination as 11 * exactly as possible, retaining modify times, flags, perms, uid, and gid. 12 * It can duplicate devices, files (including hardlinks), softlinks, 13 * directories, and so forth. It is recursive by default! The duplication 14 * is inclusive of removal of files/directories on the destination that do 15 * not exist on the source. This program supports a per-directory exception 16 * file called .cpignore, or a user-specified exception file. 17 * 18 * Safety features: 19 * 20 * - does not cross partition boundries on source 21 * - asks for confirmation on deletions unless -i0 is specified 22 * - refuses to replace a destination directory with a source file 23 * unless -s0 is specified. 24 * - terminates on error 25 * 26 * Copying features: 27 * 28 * - does not copy file if mtime, flags, perms, and size match unless 29 * forced 30 * 31 * - copies to temporary and renames-over the original, allowing 32 * you to update live systems 33 * 34 * - copies uid, gid, mtime, perms, flags, softlinks, devices, hardlinks, 35 * and recurses through directories. 36 * 37 * - accesses a per-directory exclusion file, .cpignore, containing 38 * standard wildcarded ( ? / * style, NOT regex) exclusions. 39 * 40 * - tries to play permissions and flags smart in regards to overwriting 41 * schg files and doing related stuff. 42 * 43 * - Can do MD5 consistancy checks 44 * 45 * $DragonFly: src/bin/cpdup/cpdup.c,v 1.4 2004/06/09 07:40:15 cpressey Exp $ 46 */ 47 48 /*- 49 * Example: cc -O cpdup.c -o cpdup -lmd 50 * 51 * ".MD5.CHECKSUMS" contains md5 checksumms for the current directory. 52 * This file is stored on the source. 53 */ 54 55 #include "cpdup.h" 56 57 #define HSIZE 16384 58 #define HMASK (HSIZE-1) 59 60 const char *MD5CacheFile; 61 62 typedef struct Node { 63 struct Node *no_Next; 64 struct Node *no_HNext; 65 int no_Value; 66 char no_Name[4]; 67 } Node; 68 69 typedef struct List { 70 Node li_Node; 71 Node *li_Hash[HSIZE]; 72 } List; 73 74 struct hlink { 75 ino_t ino; 76 ino_t dino; 77 char name[2048]; 78 struct hlink *next; 79 struct hlink *prev; 80 int nlinked; 81 }; 82 83 typedef struct MD5Node { 84 struct MD5Node *md_Next; 85 char *md_Name; 86 char *md_Code; 87 int md_Accessed; 88 } MD5Node; 89 90 91 void RemoveRecur(const char *dpath, dev_t devNo); 92 void InitList(List *list); 93 void ResetList(List *list); 94 int AddList(List *list, const char *name, int n); 95 struct hlink *hltlookup(struct stat *); 96 struct hlink *hltadd(struct stat *, const char *); 97 int shash(const char *s); 98 void hltdelete(struct hlink *); 99 int YesNo(const char *path); 100 int xrename(const char *src, const char *dst, u_long flags); 101 int xlink(const char *src, const char *dst, u_long flags); 102 int WildCmp(const char *s1, const char *s2); 103 MD5Node *md5_lookup(const char *sfile); 104 int md5_check(const char *spath, const char *dpath); 105 void md5_flush(void); 106 void md5_cache(const char *spath, int sdirlen); 107 char *fextract(FILE *fi, int n, int *pc, int skip); 108 int DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo); 109 char *doMD5File(const char *filename, char *buf); 110 111 int AskConfirmation = 1; 112 int SafetyOpt = 1; 113 int ForceOpt = 0; 114 int VerboseOpt = 0; 115 int QuietOpt = 0; 116 int NoRemoveOpt = 0; 117 int SaveFs = 0; 118 int UseMD5Opt = 0; 119 int SummaryOpt = 0; 120 const char *UseCpFile; 121 122 int64_t CountSourceBytes = 0; 123 int64_t CountSourceItems = 0; 124 int64_t CountCopiedBytes = 0; 125 int64_t CountCopiedItems = 0; 126 int64_t CountReadBytes = 0; 127 int64_t CountWriteBytes = 0; 128 int64_t CountRemovedItems = 0; 129 130 char *MD5SCache; /* cache source directory name */ 131 MD5Node *MD5Base; 132 int MD5SCacheDirLen; 133 int MD5SCacheDirty; 134 135 136 int 137 main(int ac, char **av) 138 { 139 int i; 140 char *src = NULL; 141 char *dst = NULL; 142 struct timeval start; 143 144 gettimeofday(&start, NULL); 145 for (i = 1; i < ac; ++i) { 146 char *ptr = av[i]; 147 int v = 1; 148 149 if (*ptr != '-') { 150 if (src == NULL) { 151 src = ptr; 152 } else if (dst == NULL) { 153 dst = ptr; 154 } else { 155 fatal("too many arguments"); 156 /* not reached */ 157 } 158 continue; 159 } 160 ptr += 2; 161 162 if (*ptr) 163 v = strtol(ptr, NULL, 0); 164 165 switch(ptr[-1]) { 166 case 'v': 167 VerboseOpt = 1; 168 while (*ptr == 'v') { 169 ++VerboseOpt; 170 ++ptr; 171 } 172 if (*ptr >= '0' && *ptr <= '9') 173 VerboseOpt = strtol(ptr, NULL, 0); 174 break; 175 case 'I': 176 SummaryOpt = v; 177 break; 178 case 'o': 179 NoRemoveOpt = v; 180 break; 181 case 'x': 182 UseCpFile = ".cpignore"; 183 break; 184 case 'X': 185 UseCpFile = (*ptr) ? ptr : av[++i]; 186 break; 187 case 'f': 188 ForceOpt = v; 189 break; 190 case 'i': 191 AskConfirmation = v; 192 break; 193 case 's': 194 SafetyOpt = v; 195 break; 196 case 'q': 197 QuietOpt = v; 198 break; 199 case 'M': 200 UseMD5Opt = v; 201 MD5CacheFile = av[++i]; 202 break; 203 case 'm': 204 UseMD5Opt = v; 205 MD5CacheFile = ".MD5.CHECKSUMS"; 206 break; 207 case 'u': 208 setvbuf(stdout, NULL, _IOLBF, 0); 209 break; 210 default: 211 fatal("illegal option: %s\n", ptr - 2); 212 /* not reached */ 213 break; 214 } 215 } 216 217 /* 218 * dst may be NULL only if -m option is specified, 219 * which forces an update of the MD5 checksums 220 */ 221 222 if (dst == NULL && UseMD5Opt == 0) { 223 fatal(NULL); 224 /* not reached */ 225 } 226 if (dst) { 227 i = DoCopy(src, dst, (dev_t)-1, (dev_t)-1); 228 } else { 229 i = DoCopy(src, NULL, (dev_t)-1, (dev_t)-1); 230 } 231 md5_flush(); 232 233 if (SummaryOpt && i == 0) { 234 long duration; 235 struct timeval end; 236 237 gettimeofday(&end, NULL); 238 CountSourceBytes += sizeof(struct stat) * CountSourceItems; 239 CountReadBytes += sizeof(struct stat) * CountSourceItems; 240 CountWriteBytes += sizeof(struct stat) * CountCopiedItems; 241 CountWriteBytes += sizeof(struct stat) * CountRemovedItems; 242 243 duration = end.tv_sec - start.tv_sec; 244 duration *= 1000000; 245 duration += end.tv_usec - start.tv_usec; 246 if (duration == 0) duration = 1; 247 logstd("cpdup completed sucessfully\n"); 248 logstd("%lld bytes source %lld bytes read %lld bytes written (%.1fX speedup)\n", 249 (long long)CountSourceBytes, 250 (long long)CountReadBytes, 251 (long long)CountWriteBytes, 252 ((double)CountSourceBytes * 2.0) / ((double)(CountReadBytes + CountWriteBytes))); 253 logstd("%lld source items %lld items copied %lld things deleted\n", 254 (long long)CountSourceItems, 255 (long long)CountCopiedItems, 256 (long long)CountRemovedItems); 257 logstd("%.1f seconds %5d Kbytes/sec synced %5d Kbytes/sec scanned\n", 258 (float)duration / (float)1000000, 259 (long)((long)1000000 * (CountReadBytes + CountWriteBytes) / duration / 1024.0), 260 (long)((long)1000000 * CountSourceBytes / duration / 1024.0)); 261 } 262 exit((i == 0) ? 0 : 1); 263 } 264 265 #define HASHF 16 266 267 struct hlink *hltable[HASHF]; 268 269 struct hlink * 270 hltlookup(struct stat *stp) 271 { 272 struct hlink *hl; 273 int n; 274 275 n = stp->st_ino % HASHF; 276 277 for (hl = hltable[n]; hl; hl = hl->next) 278 if (hl->ino == stp->st_ino) 279 return hl; 280 281 return NULL; 282 } 283 284 struct hlink * 285 hltadd(struct stat *stp, const char *path) 286 { 287 struct hlink *new; 288 int n; 289 290 if (!(new = (struct hlink *)malloc(sizeof (struct hlink)))) { 291 fprintf(stderr, "out of memory\n"); 292 exit(10); 293 } 294 295 /* initialize and link the new element into the table */ 296 new->ino = stp->st_ino; 297 new->dino = 0; 298 strncpy(new->name, path, 2048); 299 new->nlinked = 1; 300 new->prev = NULL; 301 n = stp->st_ino % HASHF; 302 new->next = hltable[n]; 303 if (hltable[n]) 304 hltable[n]->prev = new; 305 hltable[n] = new; 306 307 return new; 308 } 309 310 void 311 hltdelete(struct hlink *hl) 312 { 313 if (hl->prev) { 314 if (hl->next) 315 hl->next->prev = hl->prev; 316 hl->prev->next = hl->next; 317 } else { 318 if (hl->next) 319 hl->next->prev = NULL; 320 321 hltable[hl->ino % HASHF] = hl->next; 322 } 323 324 free(hl); 325 } 326 327 int 328 DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo) 329 { 330 struct stat st1; 331 struct stat st2; 332 int r = 0; 333 int mres = 0; 334 int st2Valid = 0; 335 struct hlink *hln = NULL; 336 List list; 337 u_int64_t size = 0; 338 339 InitList(&list); 340 341 if (lstat(spath, &st1) != 0) 342 return(0); 343 st2.st_mode = 0; /* in case lstat fails */ 344 st2.st_flags = 0; /* in case lstat fails */ 345 if (dpath && lstat(dpath, &st2) == 0) 346 st2Valid = 1; 347 348 if (S_ISREG(st1.st_mode)) { 349 size = st1.st_blocks * 512; 350 if (st1.st_size % 512) 351 size += st1.st_size % 512 - 512; 352 } 353 354 /* 355 * Handle hardlinks 356 */ 357 358 if (S_ISREG(st1.st_mode) && st1.st_nlink > 1 && dpath) { 359 if ((hln = hltlookup(&st1)) != NULL) { 360 hln->nlinked++; 361 362 if (st2Valid) { 363 if (st2.st_ino == hln->dino) { 364 /* 365 * hard link is already correct, nothing to do 366 */ 367 if (VerboseOpt >= 3) 368 logstd("%-32s nochange\n", (dpath) ? dpath : spath); 369 if (hln->nlinked == st1.st_nlink) 370 hltdelete(hln); 371 CountSourceItems++; 372 return 0; 373 } else { 374 /* 375 * hard link is not correct, attempt to unlink it 376 */ 377 if (unlink(dpath) < 0) { 378 logerr("%-32s hardlink: unable to unlink: %s\n", 379 ((dpath) ? dpath : spath), strerror(errno)); 380 hltdelete(hln); 381 return (r + 1); 382 } 383 } 384 } 385 386 if (xlink(hln->name, dpath, st1.st_flags) < 0) { 387 logerr("%-32s hardlink: unable to link to %s: %s\n", 388 (dpath ? dpath : spath), hln->name, strerror(errno) 389 ); 390 hltdelete(hln); 391 hln = NULL; 392 ++r; 393 } else { 394 if (hln->nlinked == st1.st_nlink) { 395 hltdelete(hln); 396 hln = NULL; 397 } 398 if (r == 0) { 399 if (VerboseOpt) { 400 logstd("%-32s hardlink: %s\n", 401 (dpath ? dpath : spath), 402 (st2Valid ? "relinked" : "linked") 403 ); 404 } 405 CountSourceItems++; 406 CountCopiedItems++; 407 return 0; 408 } 409 } 410 } else { 411 /* 412 * first instance of hardlink must be copied normally 413 */ 414 hln = hltadd(&st1, dpath); 415 } 416 } 417 418 /* 419 * Do we need to copy the file/dir/link/whatever? Early termination 420 * if we do not. Always traverse directories. Always redo links. 421 * 422 * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good. 423 */ 424 425 if ( 426 st2Valid && 427 st1.st_mode == st2.st_mode && 428 st1.st_flags == st2.st_flags 429 ) { 430 if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) { 431 ; 432 } else { 433 if (ForceOpt == 0 && 434 st1.st_size == st2.st_size && 435 st1.st_uid == st2.st_uid && 436 st1.st_gid == st2.st_gid && 437 st1.st_mtime == st2.st_mtime 438 && (UseMD5Opt == 0 || (mres = md5_check(spath, dpath)) == 0) 439 ) { 440 if (hln) 441 hln->dino = st2.st_ino; 442 if (VerboseOpt >= 3) { 443 if (UseMD5Opt) 444 logstd("%-32s md5-nochange\n", (dpath ? dpath : spath)); 445 else 446 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 447 } 448 CountSourceBytes += size; 449 CountSourceItems++; 450 451 return(0); 452 } 453 } 454 } 455 if (st2Valid && !S_ISDIR(st1.st_mode) && S_ISDIR(st2.st_mode)) { 456 if (SafetyOpt) { 457 logerr("%-32s SAFETY - refusing to copy file over directory\n", 458 (dpath ? dpath : spath) 459 ); 460 ++r; /* XXX */ 461 return(0); /* continue with the cpdup anyway */ 462 } 463 if (QuietOpt == 0 || AskConfirmation) { 464 logstd("%-32s WARNING: non-directory source will blow away\n" 465 "%-32s preexisting dest directory, continuing anyway!\n", 466 ((dpath) ? dpath : spath), ""); 467 } 468 if (dpath) 469 RemoveRecur(dpath, ddevNo); 470 } 471 472 if (S_ISDIR(st1.st_mode)) { 473 DIR *dir; 474 475 if ((dir = opendir(spath)) != NULL) { 476 struct dirent *den; 477 int noLoop = 0; 478 479 if (dpath) { 480 if (S_ISDIR(st2.st_mode) == 0) { 481 remove(dpath); 482 if (mkdir(dpath, st1.st_mode | 0700) != 0) { 483 logerr("%s: mkdir failed: %s\n", 484 (dpath ? dpath : spath), strerror(errno)); 485 r = 1; 486 noLoop = 1; 487 } 488 /* 489 * Matt: why don't you check error codes here? 490 */ 491 lstat(dpath, &st2); 492 chown(dpath, st1.st_uid, st1.st_gid); 493 CountCopiedItems++; 494 } else { 495 /* 496 * Directory must be scanable by root for cpdup to 497 * work. We'll fix it later if the directory isn't 498 * supposed to be readable ( which is why we fixup 499 * st2.st_mode to match what we did ). 500 */ 501 if ((st2.st_mode & 0700) != 0700) { 502 chmod(dpath, st2.st_mode | 0700); 503 st2.st_mode |= 0700; 504 } 505 if (VerboseOpt >= 2) 506 logstd("%s\n", dpath ? dpath : spath); 507 } 508 } 509 510 if ((int)sdevNo >= 0 && st1.st_dev != sdevNo) { 511 noLoop = 1; 512 } else { 513 sdevNo = st1.st_dev; 514 } 515 516 if ((int)ddevNo >= 0 && st2.st_dev != ddevNo) { 517 noLoop = 1; 518 } else { 519 ddevNo = st2.st_dev; 520 } 521 522 /* 523 * scan .cpignore file for files/directories 524 * to ignore. 525 */ 526 527 if (UseCpFile) { 528 FILE *fi; 529 char buf[8192]; 530 char *fpath; 531 532 if (UseCpFile[0] == '/') { 533 fpath = mprintf("%s", UseCpFile); 534 } else { 535 fpath = mprintf("%s/%s", spath, UseCpFile); 536 } 537 AddList(&list, strrchr(fpath, '/') + 1, 1); 538 if ((fi = fopen(fpath, "r")) != NULL) { 539 while (fgets(buf, sizeof(buf), fi) != NULL) { 540 int l = strlen(buf); 541 CountReadBytes += l; 542 if (l && buf[l-1] == '\n') 543 buf[--l] = 0; 544 if (buf[0] && buf[0] != '#') 545 AddList(&list, buf, 1); 546 } 547 fclose(fi); 548 } 549 free(fpath); 550 } 551 552 /* 553 * Automatically exclude MD5CacheFile that we create on the 554 * source from the copy to the destination. 555 */ 556 if (UseMD5Opt) 557 AddList(&list, MD5CacheFile, 1); 558 559 while (noLoop == 0 && (den = readdir(dir)) != NULL) { 560 /* 561 * ignore . and .. 562 */ 563 char *nspath; 564 char *ndpath = NULL; 565 566 if (strcmp(den->d_name, ".") == 0 || 567 strcmp(den->d_name, "..") == 0 568 ) { 569 continue; 570 } 571 /* 572 * ignore if on .cpignore list 573 */ 574 if (AddList(&list, den->d_name, 0) == 1) { 575 continue; 576 } 577 nspath = mprintf("%s/%s", spath, den->d_name); 578 if (dpath) 579 ndpath = mprintf("%s/%s", dpath, den->d_name); 580 r += DoCopy( 581 nspath, 582 ndpath, 583 sdevNo, 584 ddevNo 585 ); 586 free(nspath); 587 if (ndpath) 588 free(ndpath); 589 } 590 591 closedir(dir); 592 593 /* 594 * Remove files/directories from destination that do not appear 595 * in the source. 596 */ 597 if (dpath && (dir = opendir(dpath)) != NULL) { 598 while (noLoop == 0 && (den = readdir(dir)) != NULL) { 599 /* 600 * ignore . or .. 601 */ 602 if (strcmp(den->d_name, ".") == 0 || 603 strcmp(den->d_name, "..") == 0 604 ) { 605 continue; 606 } 607 /* 608 * If object does not exist in source or .cpignore 609 * then recursively remove it. 610 */ 611 if (AddList(&list, den->d_name, 3) == 3) { 612 char *ndpath; 613 614 ndpath = mprintf("%s/%s", dpath, den->d_name); 615 RemoveRecur(ndpath, ddevNo); 616 free(ndpath); 617 } 618 } 619 closedir(dir); 620 } 621 622 if (dpath) { 623 if (ForceOpt || 624 st2Valid == 0 || 625 st1.st_uid != st2.st_uid || 626 st1.st_gid != st2.st_gid 627 ) { 628 chown(dpath, st1.st_uid, st1.st_gid); 629 } 630 if (st2Valid == 0 || st1.st_mode != st2.st_mode) { 631 chmod(dpath, st1.st_mode); 632 } 633 if (st2Valid == 0 || st1.st_flags != st2.st_flags) { 634 chflags(dpath, st1.st_flags); 635 } 636 } 637 } 638 } else if (dpath == NULL) { 639 /* 640 * If dpath is NULL, we are just updating the MD5 641 */ 642 if (UseMD5Opt && S_ISREG(st1.st_mode)) { 643 mres = md5_check(spath, NULL); 644 645 if (VerboseOpt > 1) { 646 if (mres < 0) 647 logstd("%-32s md5-update\n", (dpath) ? dpath : spath); 648 else 649 logstd("%-32s md5-ok\n", (dpath) ? dpath : spath); 650 } else if (!QuietOpt && mres < 0) { 651 logstd("%-32s md5-update\n", (dpath) ? dpath : spath); 652 } 653 } 654 } else if (S_ISREG(st1.st_mode)) { 655 char *path; 656 int fd1; 657 int fd2; 658 659 path = mprintf("%s.tmp", dpath); 660 661 /* 662 * Handle check failure message. 663 */ 664 if (mres < 0) 665 logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath); 666 667 if ((fd1 = open(spath, O_RDONLY)) >= 0) { 668 if ((fd2 = open(path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { 669 /* 670 * There could be a .tmp file from a previously interrupted 671 * run, delete and retry. Fail if we still can't get at it. 672 */ 673 chflags(path, 0); 674 remove(path); 675 fd2 = open(path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); 676 } 677 if (fd2 >= 0) { 678 /* 679 * Matt: I think 64k would be faster here 680 */ 681 char buf[32768]; 682 const char *op; 683 int n; 684 685 /* 686 * Matt: What about holes? 687 */ 688 op = "read"; 689 while ((n = read(fd1, buf, sizeof(buf))) > 0) { 690 op = "write"; 691 if (write(fd2, buf, n) != n) 692 break; 693 op = "read"; 694 } 695 close(fd2); 696 if (n == 0) { 697 struct timeval tv[2]; 698 699 bzero(tv, sizeof(tv)); 700 tv[0].tv_sec = st1.st_mtime; 701 tv[1].tv_sec = st1.st_mtime; 702 703 utimes(path, tv); 704 chown(path, st1.st_uid, st1.st_gid); 705 chmod(path, st1.st_mode); 706 if (xrename(path, dpath, st2.st_flags) != 0) { 707 logerr("%-32s rename-after-copy failed: %s\n", 708 (dpath ? dpath : spath), strerror(errno) 709 ); 710 ++r; 711 } else { 712 if (VerboseOpt) 713 logstd("%-32s copy-ok\n", (dpath ? dpath : spath)); 714 if (st1.st_flags) 715 chflags(dpath, st1.st_flags); 716 } 717 CountReadBytes += size; 718 CountWriteBytes += size; 719 CountSourceBytes += size; 720 CountSourceItems++; 721 CountCopiedItems++; 722 } else { 723 logerr("%-32s %s failed: %s\n", 724 (dpath ? dpath : spath), op, strerror(errno) 725 ); 726 remove(path); 727 ++r; 728 } 729 } else { 730 logerr("%-32s create (uid %d, euid %d) failed: %s\n", 731 (dpath ? dpath : spath), getuid(), geteuid(), 732 strerror(errno) 733 ); 734 ++r; 735 } 736 close(fd1); 737 } else { 738 logerr("%-32s copy: open failed: %s\n", 739 (dpath ? dpath : spath), 740 strerror(errno) 741 ); 742 ++r; 743 } 744 free(path); 745 746 if (hln) { 747 if (!r && stat(dpath, &st2) == 0) 748 hln->dino = st2.st_ino; 749 else 750 hltdelete(hln); 751 } 752 } else if (S_ISLNK(st1.st_mode)) { 753 char link1[1024]; 754 char link2[1024]; 755 char path[2048]; 756 int n1; 757 int n2; 758 759 snprintf(path, sizeof(path), "%s.tmp", dpath); 760 n1 = readlink(spath, link1, sizeof(link1) - 1); 761 n2 = readlink(dpath, link2, sizeof(link2) - 1); 762 if (n1 >= 0) { 763 if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) { 764 umask(~st1.st_mode); 765 remove(path); 766 link1[n1] = 0; 767 if (symlink(link1, path) < 0) { 768 logerr("%-32s symlink (%s->%s) failed: %s\n", 769 (dpath ? dpath : spath), link1, path, 770 strerror(errno) 771 ); 772 ++r; 773 } else { 774 lchown(path, st1.st_uid, st1.st_gid); 775 /* 776 * there is no lchmod() or lchflags(), we 777 * cannot chmod or chflags a softlink. 778 */ 779 if (xrename(path, dpath, st2.st_flags) != 0) { 780 logerr("%-32s rename softlink (%s->%s) failed: %s\n", 781 (dpath ? dpath : spath), 782 path, dpath, strerror(errno)); 783 } else if (VerboseOpt) { 784 logstd("%-32s softlink-ok\n", (dpath ? dpath : spath)); 785 } 786 umask(000); 787 CountWriteBytes += n1; 788 CountCopiedItems++; 789 } 790 } else { 791 if (VerboseOpt >= 3) 792 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 793 } 794 CountSourceBytes += n1; 795 CountReadBytes += n1; 796 if (n2 > 0) CountReadBytes += n2; 797 CountSourceItems++; 798 } else { 799 r = 1; 800 logerr("%-32s softlink-failed\n", (dpath ? dpath : spath)); 801 } 802 } else if (S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) { 803 char path[2048]; 804 805 if (ForceOpt || 806 st2Valid == 0 || 807 st1.st_mode != st2.st_mode || 808 st1.st_rdev != st2.st_rdev || 809 st1.st_uid != st2.st_uid || 810 st1.st_gid != st2.st_gid 811 ) { 812 snprintf(path, sizeof(path), "%s.tmp", dpath); 813 814 remove(path); 815 if (mknod(path, st1.st_mode, st1.st_rdev) == 0) { 816 chmod(path, st1.st_mode); 817 chown(path, st1.st_uid, st1.st_gid); 818 remove(dpath); 819 if (xrename(path, dpath, st2.st_flags) != 0) { 820 logerr("%-32s dev-rename-after-create failed: %s\n", 821 (dpath ? dpath : spath), 822 strerror(errno) 823 ); 824 } else if (VerboseOpt) { 825 logstd("%-32s dev-ok\n", (dpath ? dpath : spath)); 826 } 827 CountCopiedItems++; 828 } else { 829 r = 1; 830 logerr("%-32s dev failed: %s\n", 831 (dpath ? dpath : spath), strerror(errno) 832 ); 833 } 834 } else { 835 if (VerboseOpt >= 3) 836 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 837 } 838 CountSourceItems++; 839 } 840 ResetList(&list); 841 return(r); 842 } 843 844 /* 845 * RemoveRecur() 846 */ 847 848 void 849 RemoveRecur(const char *dpath, dev_t devNo) 850 { 851 struct stat st; 852 853 if (lstat(dpath, &st) == 0) { 854 if ((int)devNo < 0) 855 devNo = st.st_dev; 856 if (st.st_dev == devNo) { 857 if (S_ISDIR(st.st_mode)) { 858 DIR *dir; 859 860 if ((dir = opendir(dpath)) != NULL) { 861 struct dirent *den; 862 while ((den = readdir(dir)) != NULL) { 863 char *ndpath; 864 865 if (strcmp(den->d_name, ".") == 0) 866 continue; 867 if (strcmp(den->d_name, "..") == 0) 868 continue; 869 ndpath = mprintf("%s/%s", dpath, den->d_name); 870 RemoveRecur(ndpath, devNo); 871 free(ndpath); 872 } 873 closedir(dir); 874 } 875 if (AskConfirmation && NoRemoveOpt == 0) { 876 if (YesNo(dpath)) { 877 if (rmdir(dpath) < 0) { 878 logerr("%-32s rmdir failed: %s\n", 879 dpath, strerror(errno) 880 ); 881 } 882 CountRemovedItems++; 883 } 884 } else { 885 if (NoRemoveOpt) { 886 if (VerboseOpt) 887 logstd("%-32s not-removed\n", dpath); 888 } else if (rmdir(dpath) == 0) { 889 if (VerboseOpt) 890 logstd("%-32s rmdir-ok\n", dpath); 891 CountRemovedItems++; 892 } else { 893 logerr("%-32s rmdir failed: %s\n", 894 dpath, strerror(errno) 895 ); 896 } 897 } 898 } else { 899 if (AskConfirmation && NoRemoveOpt == 0) { 900 if (YesNo(dpath)) { 901 if (remove(dpath) < 0) { 902 logerr("%-32s remove failed: %s\n", 903 dpath, strerror(errno) 904 ); 905 } 906 CountRemovedItems++; 907 } 908 } else { 909 if (NoRemoveOpt) { 910 if (VerboseOpt) 911 logstd("%-32s not-removed\n", dpath); 912 } else if (remove(dpath) == 0) { 913 if (VerboseOpt) 914 logstd("%-32s remove-ok\n", dpath); 915 CountRemovedItems++; 916 } else { 917 logerr("%-32s remove failed: %s\n", 918 dpath, strerror(errno) 919 ); 920 } 921 } 922 } 923 } 924 } 925 } 926 927 void 928 InitList(List *list) 929 { 930 bzero(list, sizeof(List)); 931 list->li_Node.no_Next = &list->li_Node; 932 } 933 934 void 935 ResetList(List *list) 936 { 937 Node *node; 938 939 while ((node = list->li_Node.no_Next) != &list->li_Node) { 940 list->li_Node.no_Next = node->no_Next; 941 free(node); 942 } 943 InitList(list); 944 } 945 946 int 947 AddList(List *list, const char *name, int n) 948 { 949 Node *node; 950 int hv = shash(name); 951 952 /* 953 * Scan against wildcards. Only a node value of 1 can be a wildcard 954 * ( usually scanned from .cpignore ) 955 */ 956 957 for (node = list->li_Hash[0]; node; node = node->no_HNext) { 958 if (strcmp(name, node->no_Name) == 0 || 959 (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0) 960 ) { 961 return(node->no_Value); 962 } 963 } 964 965 /* 966 * Look for exact match 967 */ 968 969 for (node = list->li_Hash[hv]; node; node = node->no_HNext) { 970 if (strcmp(name, node->no_Name) == 0) { 971 return(node->no_Value); 972 } 973 } 974 node = malloc(sizeof(Node) + strlen(name) + 1); 975 976 node->no_Next = list->li_Node.no_Next; 977 list->li_Node.no_Next = node; 978 979 node->no_HNext = list->li_Hash[hv]; 980 list->li_Hash[hv] = node; 981 982 strcpy(node->no_Name, name); 983 node->no_Value = n; 984 985 return(n); 986 } 987 988 int 989 shash(const char *s) 990 { 991 int hv = 0xA4FB3255; 992 993 while (*s) { 994 if (*s == '*' || *s == '?' || 995 *s == '{' || *s == '}' || 996 *s == '[' || *s == ']' || 997 *s == '|' 998 ) { 999 return(0); 1000 } 1001 hv = (hv << 5) ^ *s ^ (hv >> 23); 1002 ++s; 1003 } 1004 return(((hv >> 16) ^ hv) & HMASK); 1005 } 1006 1007 /* 1008 * WildCmp() - compare wild string to sane string 1009 * 1010 * Return 0 on success, -1 on failure. 1011 */ 1012 1013 int 1014 WildCmp(const char *w, const char *s) 1015 { 1016 /* 1017 * skip fixed portion 1018 */ 1019 1020 for (;;) { 1021 switch(*w) { 1022 case '*': 1023 if (w[1] == 0) /* optimize wild* case */ 1024 return(0); 1025 { 1026 int i; 1027 int l = strlen(s); 1028 1029 for (i = 0; i <= l; ++i) { 1030 if (WildCmp(w + 1, s + i) == 0) 1031 return(0); 1032 } 1033 } 1034 return(-1); 1035 case '?': 1036 if (*s == 0) 1037 return(-1); 1038 ++w; 1039 ++s; 1040 break; 1041 default: 1042 if (*w != *s) 1043 return(-1); 1044 if (*w == 0) /* terminator */ 1045 return(0); 1046 ++w; 1047 ++s; 1048 break; 1049 } 1050 } 1051 /* not reached */ 1052 return(-1); 1053 } 1054 1055 int 1056 YesNo(const char *path) 1057 { 1058 int ch, first; 1059 1060 (void)fprintf(stderr, "remove %s (Yes/No) [No]? ", path); 1061 (void)fflush(stderr); 1062 1063 first = ch = getchar(); 1064 while (ch != '\n' && ch != EOF) 1065 ch = getchar(); 1066 return ((first == 'y' || first == 'Y')); 1067 } 1068 1069 void 1070 md5_flush(void) 1071 { 1072 if (MD5SCacheDirty && MD5SCache) { 1073 FILE *fo; 1074 1075 if ((fo = fopen(MD5SCache, "w")) != NULL) { 1076 MD5Node *node; 1077 1078 for (node = MD5Base; node; node = node->md_Next) { 1079 if (node->md_Accessed && node->md_Code) { 1080 fprintf(fo, "%s %d %s\n", 1081 node->md_Code, 1082 strlen(node->md_Name), 1083 node->md_Name 1084 ); 1085 } 1086 } 1087 fclose(fo); 1088 } 1089 } 1090 1091 MD5SCacheDirty = 0; 1092 1093 if (MD5SCache) { 1094 MD5Node *node; 1095 1096 while ((node = MD5Base) != NULL) { 1097 MD5Base = node->md_Next; 1098 1099 if (node->md_Code) 1100 free(node->md_Code); 1101 if (node->md_Name) 1102 free(node->md_Name); 1103 free(node); 1104 } 1105 free(MD5SCache); 1106 MD5SCache = NULL; 1107 } 1108 } 1109 1110 void 1111 md5_cache(const char *spath, int sdirlen) 1112 { 1113 FILE *fi; 1114 1115 /* 1116 * Already cached 1117 */ 1118 1119 if ( 1120 MD5SCache && 1121 sdirlen == MD5SCacheDirLen && 1122 strncmp(spath, MD5SCache, sdirlen) == 0 1123 ) { 1124 return; 1125 } 1126 1127 /* 1128 * Different cache, flush old cache 1129 */ 1130 1131 if (MD5SCache != NULL) 1132 md5_flush(); 1133 1134 /* 1135 * Create new cache 1136 */ 1137 1138 MD5SCacheDirLen = sdirlen; 1139 MD5SCache = mprintf("%*.*s%s", sdirlen, sdirlen, spath, MD5CacheFile); 1140 1141 if ((fi = fopen(MD5SCache, "r")) != NULL) { 1142 MD5Node **pnode = &MD5Base; 1143 int c; 1144 1145 c = fgetc(fi); 1146 while (c != EOF) { 1147 MD5Node *node = *pnode = malloc(sizeof(MD5Node)); 1148 char *s; 1149 int nlen = 0; 1150 1151 bzero(node, sizeof(MD5Node)); 1152 node->md_Code = fextract(fi, -1, &c, ' '); 1153 node->md_Accessed = 1; 1154 if ((s = fextract(fi, -1, &c, ' ')) != NULL) { 1155 nlen = strtol(s, NULL, 0); 1156 free(s); 1157 } 1158 /* 1159 * extracting md_Name - name may contain embedded control 1160 * characters. 1161 */ 1162 CountReadBytes += nlen+1; 1163 node->md_Name = fextract(fi, nlen, &c, EOF); 1164 if (c != '\n') { 1165 fprintf(stderr, "Error parsing MD5 Cache: %s (%c)\n", MD5SCache, c); 1166 while (c != EOF && c != '\n') 1167 c = fgetc(fi); 1168 } 1169 if (c != EOF) 1170 c = fgetc(fi); 1171 pnode = &node->md_Next; 1172 } 1173 fclose(fi); 1174 } 1175 } 1176 1177 /* 1178 * md5_lookup: lookup/create md5 entry 1179 */ 1180 1181 MD5Node * 1182 md5_lookup(const char *sfile) 1183 { 1184 MD5Node **pnode; 1185 MD5Node *node; 1186 1187 for (pnode = &MD5Base; (node = *pnode) != NULL; pnode = &node->md_Next) { 1188 if (strcmp(sfile, node->md_Name) == 0) { 1189 break; 1190 } 1191 } 1192 if (node == NULL) { 1193 node = *pnode = malloc(sizeof(MD5Node)); 1194 bzero(node, sizeof(MD5Node)); 1195 node->md_Name = strdup(sfile); 1196 } 1197 node->md_Accessed = 1; 1198 return(node); 1199 } 1200 1201 /* 1202 * md5_check: check MD5 against file 1203 * 1204 * Return -1 if check failed 1205 * Return 0 if check succeeded 1206 * 1207 * dpath can be NULL, in which case we are force-updating 1208 * the source MD5. 1209 */ 1210 1211 int 1212 md5_check(const char *spath, const char *dpath) 1213 { 1214 const char *sfile; 1215 char *dcode; 1216 int sdirlen; 1217 int r = -1; 1218 MD5Node *node; 1219 1220 if ((sfile = strrchr(spath, '/')) != NULL) 1221 ++sfile; 1222 else 1223 sfile = spath; 1224 sdirlen = sfile - spath; 1225 1226 md5_cache(spath, sdirlen); 1227 1228 node = md5_lookup(sfile); 1229 1230 /* 1231 * If dpath == NULL, we are force-updating the source .MD5* files 1232 */ 1233 1234 if (dpath == NULL) { 1235 char *scode = doMD5File(spath, NULL); 1236 1237 r = 0; 1238 if (node->md_Code == NULL) { 1239 r = -1; 1240 node->md_Code = scode; 1241 MD5SCacheDirty = 1; 1242 } else if (strcmp(scode, node->md_Code) != 0) { 1243 r = -1; 1244 free(node->md_Code); 1245 node->md_Code = scode; 1246 MD5SCacheDirty = 1; 1247 } else { 1248 free(scode); 1249 } 1250 return(r); 1251 } 1252 1253 /* 1254 * Otherwise the .MD5* file is used as a cache. 1255 */ 1256 1257 if (node->md_Code == NULL) { 1258 node->md_Code = doMD5File(spath, NULL); 1259 MD5SCacheDirty = 1; 1260 } 1261 1262 dcode = doMD5File(dpath, NULL); 1263 if (dcode) { 1264 if (strcmp(node->md_Code, dcode) == 0) { 1265 r = 0; 1266 } else { 1267 char *scode = doMD5File(spath, NULL); 1268 1269 if (strcmp(node->md_Code, scode) == 0) { 1270 free(scode); 1271 } else { 1272 free(node->md_Code); 1273 node->md_Code = scode; 1274 MD5SCacheDirty = 1; 1275 if (strcmp(node->md_Code, dcode) == 0) 1276 r = 0; 1277 } 1278 } 1279 free(dcode); 1280 } 1281 return(r); 1282 } 1283 1284 /* 1285 * xrename() - rename with override 1286 * 1287 * If the rename fails, attempt to override st_flags on the 1288 * destination and rename again. If that fails too, try to 1289 * set the flags back the way they were and give up. 1290 */ 1291 1292 int 1293 xrename(const char *src, const char *dst, u_long flags) 1294 { 1295 int r = 0; 1296 1297 if ((r = rename(src, dst)) < 0) { 1298 chflags(dst, 0); 1299 if ((r = rename(src, dst)) < 0) 1300 chflags(dst, flags); 1301 } 1302 return(r); 1303 } 1304 1305 int 1306 xlink(const char *src, const char *dst, u_long flags) 1307 { 1308 int r = 0; 1309 int e; 1310 1311 if ((r = link(src, dst)) < 0) { 1312 chflags(src, 0); 1313 r = link(src, dst); 1314 e = errno; 1315 chflags(src, flags); 1316 errno = e; 1317 } 1318 return(r); 1319 } 1320 1321 char * 1322 fextract(FILE *fi, int n, int *pc, int skip) 1323 { 1324 int i = 0; 1325 int imax = (n < 0) ? 64 : n + 1; 1326 char *s = malloc(imax); 1327 int c = *pc; 1328 1329 while (c != EOF) { 1330 if (n == 0 || (n < 0 && (c == ' ' || c == '\n'))) 1331 break; 1332 1333 s[i++] = c; 1334 if (i == imax) { 1335 imax += 64; 1336 s = realloc(s, imax); 1337 } 1338 if (n > 0) 1339 --n; 1340 c = getc(fi); 1341 } 1342 if (c == skip && skip != EOF) 1343 c = getc(fi); 1344 *pc = c; 1345 s[i] = 0; 1346 return(s); 1347 } 1348 1349 char * 1350 doMD5File(const char *filename, char *buf) 1351 { 1352 if (SummaryOpt) { 1353 struct stat st; 1354 if (stat(filename, &st) == 0) { 1355 u_int64_t size = st.st_blocks * 512; 1356 if (st.st_size % 512) 1357 size += st.st_size % 512 - 512; 1358 CountReadBytes += size; 1359 } 1360 } 1361 return MD5File(filename, buf); 1362 } 1363 1364