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