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