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