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