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