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.11 2006/07/04 00:32:03 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 logerr("%-32s hardlink: unable to link to %s: %s\n", 451 (dpath ? dpath : spath), hln->name, strerror(errno) 452 ); 453 hltdelete(hln); 454 hln = NULL; 455 ++r; 456 } else { 457 if (hln->nlinked == st1.st_nlink) { 458 hltdelete(hln); 459 hln = NULL; 460 } 461 if (r == 0) { 462 if (VerboseOpt) { 463 logstd("%-32s hardlink: %s\n", 464 (dpath ? dpath : spath), 465 (st2Valid ? "relinked" : "linked") 466 ); 467 } 468 CountSourceItems++; 469 CountCopiedItems++; 470 return 0; 471 } 472 } 473 } else { 474 /* 475 * first instance of hardlink must be copied normally 476 */ 477 hln = hltadd(&st1, dpath); 478 } 479 } 480 481 /* 482 * Do we need to copy the file/dir/link/whatever? Early termination 483 * if we do not. Always redo links. Directories are always traversed 484 * except when the FSMID options are used. 485 * 486 * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good. 487 */ 488 489 if ( 490 st2Valid && 491 st1.st_mode == st2.st_mode && 492 st1.st_flags == st2.st_flags 493 ) { 494 if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) { 495 /* 496 * If FSMID tracking is turned on we can avoid recursing through 497 * an entire directory subtree if the FSMID matches. 498 */ 499 #ifdef _ST_FSMID_PRESENT_ 500 if (ForceOpt == 0 && 501 (UseFSMIDOpt && (fres = fsmid_check(st1.st_fsmid, dpath)) == 0) 502 ) { 503 if (VerboseOpt >= 3) { 504 if (UseFSMIDOpt) 505 logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath)); 506 else 507 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 508 } 509 return(0); 510 } 511 #endif 512 } else { 513 if (ForceOpt == 0 && 514 st1.st_size == st2.st_size && 515 st1.st_uid == st2.st_uid && 516 st1.st_gid == st2.st_gid && 517 st1.st_mtime == st2.st_mtime 518 && (UseMD5Opt == 0 || (mres = md5_check(spath, dpath)) == 0) 519 #ifdef _ST_FSMID_PRESENT_ 520 && (UseFSMIDOpt == 0 || (fres = fsmid_check(st1.st_fsmid, dpath)) == 0) 521 #endif 522 ) { 523 if (hln) 524 hln->dino = st2.st_ino; 525 if (VerboseOpt >= 3) { 526 if (UseMD5Opt) 527 logstd("%-32s md5-nochange\n", (dpath ? dpath : spath)); 528 else if (UseFSMIDOpt) 529 logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath)); 530 else 531 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 532 } 533 CountSourceBytes += size; 534 CountSourceItems++; 535 536 return(0); 537 } 538 } 539 } 540 if (st2Valid && !S_ISDIR(st1.st_mode) && S_ISDIR(st2.st_mode)) { 541 if (SafetyOpt) { 542 logerr("%-32s SAFETY - refusing to copy file over directory\n", 543 (dpath ? dpath : spath) 544 ); 545 ++r; /* XXX */ 546 return(0); /* continue with the cpdup anyway */ 547 } 548 if (QuietOpt == 0 || AskConfirmation) { 549 logstd("%-32s WARNING: non-directory source will blow away\n" 550 "%-32s preexisting dest directory, continuing anyway!\n", 551 ((dpath) ? dpath : spath), ""); 552 } 553 if (dpath) 554 RemoveRecur(dpath, ddevNo); 555 } 556 557 /* 558 * The various comparisons failed, copy it. 559 */ 560 if (S_ISDIR(st1.st_mode)) { 561 DIR *dir; 562 563 if (fres < 0) 564 logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath); 565 if ((dir = opendir(spath)) != NULL) { 566 struct dirent *den; 567 int noLoop = 0; 568 569 if (dpath) { 570 if (S_ISDIR(st2.st_mode) == 0) { 571 remove(dpath); 572 if (mkdir(dpath, st1.st_mode | 0700) != 0) { 573 logerr("%s: mkdir failed: %s\n", 574 (dpath ? dpath : spath), strerror(errno)); 575 r = 1; 576 noLoop = 1; 577 } 578 /* 579 * Matt: why don't you check error codes here? 580 */ 581 lstat(dpath, &st2); 582 chown(dpath, st1.st_uid, st1.st_gid); 583 CountCopiedItems++; 584 } else { 585 /* 586 * Directory must be scanable by root for cpdup to 587 * work. We'll fix it later if the directory isn't 588 * supposed to be readable ( which is why we fixup 589 * st2.st_mode to match what we did ). 590 */ 591 if ((st2.st_mode & 0700) != 0700) { 592 chmod(dpath, st2.st_mode | 0700); 593 st2.st_mode |= 0700; 594 } 595 if (VerboseOpt >= 2) 596 logstd("%s\n", dpath ? dpath : spath); 597 } 598 } 599 600 if ((int)sdevNo >= 0 && st1.st_dev != sdevNo) { 601 noLoop = 1; 602 } else { 603 sdevNo = st1.st_dev; 604 } 605 606 if ((int)ddevNo >= 0 && st2.st_dev != ddevNo) { 607 noLoop = 1; 608 } else { 609 ddevNo = st2.st_dev; 610 } 611 612 /* 613 * scan .cpignore file for files/directories 614 * to ignore. 615 */ 616 617 if (UseCpFile) { 618 FILE *fi; 619 char buf[8192]; 620 char *fpath; 621 622 if (UseCpFile[0] == '/') { 623 fpath = mprintf("%s", UseCpFile); 624 } else { 625 fpath = mprintf("%s/%s", spath, UseCpFile); 626 } 627 AddList(&list, strrchr(fpath, '/') + 1, 1); 628 if ((fi = fopen(fpath, "r")) != NULL) { 629 while (fgets(buf, sizeof(buf), fi) != NULL) { 630 int l = strlen(buf); 631 CountReadBytes += l; 632 if (l && buf[l-1] == '\n') 633 buf[--l] = 0; 634 if (buf[0] && buf[0] != '#') 635 AddList(&list, buf, 1); 636 } 637 fclose(fi); 638 } 639 free(fpath); 640 } 641 642 /* 643 * Automatically exclude MD5CacheFile that we create on the 644 * source from the copy to the destination. 645 * 646 * Automatically exclude a FSMIDCacheFile on the source that 647 * would otherwise overwrite the one we maintain on the target. 648 */ 649 if (UseMD5Opt) 650 AddList(&list, MD5CacheFile, 1); 651 if (UseFSMIDOpt) 652 AddList(&list, FSMIDCacheFile, 1); 653 654 while (noLoop == 0 && (den = readdir(dir)) != NULL) { 655 /* 656 * ignore . and .. 657 */ 658 char *nspath; 659 char *ndpath = NULL; 660 661 if (strcmp(den->d_name, ".") == 0 || 662 strcmp(den->d_name, "..") == 0 663 ) { 664 continue; 665 } 666 /* 667 * ignore if on .cpignore list 668 */ 669 if (AddList(&list, den->d_name, 0) == 1) { 670 continue; 671 } 672 nspath = mprintf("%s/%s", spath, den->d_name); 673 if (dpath) 674 ndpath = mprintf("%s/%s", dpath, den->d_name); 675 r += DoCopy( 676 nspath, 677 ndpath, 678 sdevNo, 679 ddevNo 680 ); 681 free(nspath); 682 if (ndpath) 683 free(ndpath); 684 } 685 686 closedir(dir); 687 688 /* 689 * Remove files/directories from destination that do not appear 690 * in the source. 691 */ 692 if (dpath && (dir = opendir(dpath)) != NULL) { 693 while (noLoop == 0 && (den = readdir(dir)) != NULL) { 694 /* 695 * ignore . or .. 696 */ 697 if (strcmp(den->d_name, ".") == 0 || 698 strcmp(den->d_name, "..") == 0 699 ) { 700 continue; 701 } 702 /* 703 * If object does not exist in source or .cpignore 704 * then recursively remove it. 705 */ 706 if (AddList(&list, den->d_name, 3) == 3) { 707 char *ndpath; 708 709 ndpath = mprintf("%s/%s", dpath, den->d_name); 710 RemoveRecur(ndpath, ddevNo); 711 free(ndpath); 712 } 713 } 714 closedir(dir); 715 } 716 717 if (dpath) { 718 if (ForceOpt || 719 st2Valid == 0 || 720 st1.st_uid != st2.st_uid || 721 st1.st_gid != st2.st_gid 722 ) { 723 chown(dpath, st1.st_uid, st1.st_gid); 724 } 725 if (st2Valid == 0 || st1.st_mode != st2.st_mode) { 726 chmod(dpath, st1.st_mode); 727 } 728 if (st2Valid == 0 || st1.st_flags != st2.st_flags) { 729 chflags(dpath, st1.st_flags); 730 } 731 } 732 } 733 } else if (dpath == NULL) { 734 /* 735 * If dpath is NULL, we are just updating the MD5 736 */ 737 if (UseMD5Opt && S_ISREG(st1.st_mode)) { 738 mres = md5_check(spath, NULL); 739 740 if (VerboseOpt > 1) { 741 if (mres < 0) 742 logstd("%-32s md5-update\n", (dpath) ? dpath : spath); 743 else 744 logstd("%-32s md5-ok\n", (dpath) ? dpath : spath); 745 } else if (!QuietOpt && mres < 0) { 746 logstd("%-32s md5-update\n", (dpath) ? dpath : spath); 747 } 748 } 749 } else if (S_ISREG(st1.st_mode)) { 750 char *path; 751 char *hpath; 752 int fd1; 753 int fd2; 754 755 path = mprintf("%s.tmp", dpath); 756 757 /* 758 * Handle check failure message. 759 */ 760 if (mres < 0) 761 logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath); 762 else if (fres < 0) 763 logerr("%-32s fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath); 764 765 /* 766 * Not quite ready to do the copy yet. If UseHLPath is defined, 767 * see if we can hardlink instead. 768 */ 769 770 if (UseHLPath && (hpath = checkHLPath(&st1, spath, dpath)) != NULL) { 771 if (link(hpath, dpath) == 0) { 772 if (VerboseOpt) { 773 logstd("%-32s hardlinked(-H)\n", 774 (dpath ? dpath : spath)); 775 } 776 free(hpath); 777 goto skip_copy; 778 } 779 /* 780 * Shucks, we may have hit a filesystem hard linking limit, 781 * we have to copy instead. 782 */ 783 free(hpath); 784 } 785 786 if ((fd1 = open(spath, O_RDONLY)) >= 0) { 787 if ((fd2 = open(path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { 788 /* 789 * There could be a .tmp file from a previously interrupted 790 * run, delete and retry. Fail if we still can't get at it. 791 */ 792 chflags(path, 0); 793 remove(path); 794 fd2 = open(path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); 795 } 796 if (fd2 >= 0) { 797 const char *op; 798 int n; 799 800 /* 801 * Matt: What about holes? 802 */ 803 op = "read"; 804 while ((n = read(fd1, IOBuf1, sizeof(IOBuf1))) > 0) { 805 op = "write"; 806 if (write(fd2, IOBuf1, n) != n) 807 break; 808 op = "read"; 809 } 810 close(fd2); 811 if (n == 0) { 812 struct timeval tv[2]; 813 814 bzero(tv, sizeof(tv)); 815 tv[0].tv_sec = st1.st_mtime; 816 tv[1].tv_sec = st1.st_mtime; 817 818 utimes(path, tv); 819 chown(path, st1.st_uid, st1.st_gid); 820 chmod(path, st1.st_mode); 821 if (xrename(path, dpath, st2.st_flags) != 0) { 822 logerr("%-32s rename-after-copy failed: %s\n", 823 (dpath ? dpath : spath), strerror(errno) 824 ); 825 ++r; 826 } else { 827 if (VerboseOpt) 828 logstd("%-32s copy-ok\n", (dpath ? dpath : spath)); 829 if (st1.st_flags) 830 chflags(dpath, st1.st_flags); 831 } 832 CountReadBytes += size; 833 CountWriteBytes += size; 834 CountSourceBytes += size; 835 CountSourceItems++; 836 CountCopiedItems++; 837 } else { 838 logerr("%-32s %s failed: %s\n", 839 (dpath ? dpath : spath), op, strerror(errno) 840 ); 841 remove(path); 842 ++r; 843 } 844 } else { 845 logerr("%-32s create (uid %d, euid %d) failed: %s\n", 846 (dpath ? dpath : spath), getuid(), geteuid(), 847 strerror(errno) 848 ); 849 ++r; 850 } 851 close(fd1); 852 } else { 853 logerr("%-32s copy: open failed: %s\n", 854 (dpath ? dpath : spath), 855 strerror(errno) 856 ); 857 ++r; 858 } 859 skip_copy: 860 free(path); 861 862 if (hln) { 863 if (!r && stat(dpath, &st2) == 0) 864 hln->dino = st2.st_ino; 865 else 866 hltdelete(hln); 867 } 868 } else if (S_ISLNK(st1.st_mode)) { 869 char link1[1024]; 870 char link2[1024]; 871 char path[2048]; 872 int n1; 873 int n2; 874 875 snprintf(path, sizeof(path), "%s.tmp", dpath); 876 n1 = readlink(spath, link1, sizeof(link1) - 1); 877 n2 = readlink(dpath, link2, sizeof(link2) - 1); 878 if (n1 >= 0) { 879 if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) { 880 umask(~st1.st_mode); 881 remove(path); 882 link1[n1] = 0; 883 if (symlink(link1, path) < 0) { 884 logerr("%-32s symlink (%s->%s) failed: %s\n", 885 (dpath ? dpath : spath), link1, path, 886 strerror(errno) 887 ); 888 ++r; 889 } else { 890 lchown(path, st1.st_uid, st1.st_gid); 891 /* 892 * there is no lchmod() or lchflags(), we 893 * cannot chmod or chflags a softlink. 894 */ 895 if (xrename(path, dpath, st2.st_flags) != 0) { 896 logerr("%-32s rename softlink (%s->%s) failed: %s\n", 897 (dpath ? dpath : spath), 898 path, dpath, strerror(errno)); 899 } else if (VerboseOpt) { 900 logstd("%-32s softlink-ok\n", (dpath ? dpath : spath)); 901 } 902 umask(000); 903 CountWriteBytes += n1; 904 CountCopiedItems++; 905 } 906 } else { 907 if (VerboseOpt >= 3) 908 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 909 } 910 CountSourceBytes += n1; 911 CountReadBytes += n1; 912 if (n2 > 0) CountReadBytes += n2; 913 CountSourceItems++; 914 } else { 915 r = 1; 916 logerr("%-32s softlink-failed\n", (dpath ? dpath : spath)); 917 } 918 } else if (S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) { 919 char path[2048]; 920 921 if (ForceOpt || 922 st2Valid == 0 || 923 st1.st_mode != st2.st_mode || 924 st1.st_rdev != st2.st_rdev || 925 st1.st_uid != st2.st_uid || 926 st1.st_gid != st2.st_gid 927 ) { 928 snprintf(path, sizeof(path), "%s.tmp", dpath); 929 930 remove(path); 931 if (mknod(path, st1.st_mode, st1.st_rdev) == 0) { 932 chmod(path, st1.st_mode); 933 chown(path, st1.st_uid, st1.st_gid); 934 remove(dpath); 935 if (xrename(path, dpath, st2.st_flags) != 0) { 936 logerr("%-32s dev-rename-after-create failed: %s\n", 937 (dpath ? dpath : spath), 938 strerror(errno) 939 ); 940 } else if (VerboseOpt) { 941 logstd("%-32s dev-ok\n", (dpath ? dpath : spath)); 942 } 943 CountCopiedItems++; 944 } else { 945 r = 1; 946 logerr("%-32s dev failed: %s\n", 947 (dpath ? dpath : spath), strerror(errno) 948 ); 949 } 950 } else { 951 if (VerboseOpt >= 3) 952 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 953 } 954 CountSourceItems++; 955 } 956 ResetList(&list); 957 return (r); 958 } 959 960 /* 961 * RemoveRecur() 962 */ 963 964 void 965 RemoveRecur(const char *dpath, dev_t devNo) 966 { 967 struct stat st; 968 969 if (lstat(dpath, &st) == 0) { 970 if ((int)devNo < 0) 971 devNo = st.st_dev; 972 if (st.st_dev == devNo) { 973 if (S_ISDIR(st.st_mode)) { 974 DIR *dir; 975 976 if ((dir = opendir(dpath)) != NULL) { 977 struct dirent *den; 978 while ((den = readdir(dir)) != NULL) { 979 char *ndpath; 980 981 if (strcmp(den->d_name, ".") == 0) 982 continue; 983 if (strcmp(den->d_name, "..") == 0) 984 continue; 985 ndpath = mprintf("%s/%s", dpath, den->d_name); 986 RemoveRecur(ndpath, devNo); 987 free(ndpath); 988 } 989 closedir(dir); 990 } 991 if (AskConfirmation && NoRemoveOpt == 0) { 992 if (YesNo(dpath)) { 993 if (rmdir(dpath) < 0) { 994 logerr("%-32s rmdir failed: %s\n", 995 dpath, strerror(errno) 996 ); 997 } 998 CountRemovedItems++; 999 } 1000 } else { 1001 if (NoRemoveOpt) { 1002 if (VerboseOpt) 1003 logstd("%-32s not-removed\n", dpath); 1004 } else if (rmdir(dpath) == 0) { 1005 if (VerboseOpt) 1006 logstd("%-32s rmdir-ok\n", dpath); 1007 CountRemovedItems++; 1008 } else { 1009 logerr("%-32s rmdir failed: %s\n", 1010 dpath, strerror(errno) 1011 ); 1012 } 1013 } 1014 } else { 1015 if (AskConfirmation && NoRemoveOpt == 0) { 1016 if (YesNo(dpath)) { 1017 if (remove(dpath) < 0) { 1018 logerr("%-32s remove failed: %s\n", 1019 dpath, strerror(errno) 1020 ); 1021 } 1022 CountRemovedItems++; 1023 } 1024 } else { 1025 if (NoRemoveOpt) { 1026 if (VerboseOpt) 1027 logstd("%-32s not-removed\n", dpath); 1028 } else if (remove(dpath) == 0) { 1029 if (VerboseOpt) 1030 logstd("%-32s remove-ok\n", dpath); 1031 CountRemovedItems++; 1032 } else { 1033 logerr("%-32s remove failed: %s\n", 1034 dpath, strerror(errno) 1035 ); 1036 } 1037 } 1038 } 1039 } 1040 } 1041 } 1042 1043 void 1044 InitList(List *list) 1045 { 1046 bzero(list, sizeof(List)); 1047 list->li_Node.no_Next = &list->li_Node; 1048 } 1049 1050 void 1051 ResetList(List *list) 1052 { 1053 Node *node; 1054 1055 while ((node = list->li_Node.no_Next) != &list->li_Node) { 1056 list->li_Node.no_Next = node->no_Next; 1057 free(node); 1058 } 1059 InitList(list); 1060 } 1061 1062 int 1063 AddList(List *list, const char *name, int n) 1064 { 1065 Node *node; 1066 int hv; 1067 1068 hv = shash(name); 1069 1070 /* 1071 * Scan against wildcards. Only a node value of 1 can be a wildcard 1072 * ( usually scanned from .cpignore ) 1073 */ 1074 1075 for (node = list->li_Hash[0]; node; node = node->no_HNext) { 1076 if (strcmp(name, node->no_Name) == 0 || 1077 (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0) 1078 ) { 1079 return(node->no_Value); 1080 } 1081 } 1082 1083 /* 1084 * Look for exact match 1085 */ 1086 1087 for (node = list->li_Hash[hv]; node; node = node->no_HNext) { 1088 if (strcmp(name, node->no_Name) == 0) { 1089 return(node->no_Value); 1090 } 1091 } 1092 node = malloc(sizeof(Node) + strlen(name) + 1); 1093 if (node == NULL) { 1094 fprintf(stderr, "out of memory\n"); 1095 exit(EXIT_FAILURE); 1096 } 1097 1098 node->no_Next = list->li_Node.no_Next; 1099 list->li_Node.no_Next = node; 1100 1101 node->no_HNext = list->li_Hash[hv]; 1102 list->li_Hash[hv] = node; 1103 1104 strcpy(node->no_Name, name); 1105 node->no_Value = n; 1106 1107 return(n); 1108 } 1109 1110 static int 1111 shash(const char *s) 1112 { 1113 int hv; 1114 1115 hv = 0xA4FB3255; 1116 1117 while (*s) { 1118 if (*s == '*' || *s == '?' || 1119 *s == '{' || *s == '}' || 1120 *s == '[' || *s == ']' || 1121 *s == '|' 1122 ) { 1123 return(0); 1124 } 1125 hv = (hv << 5) ^ *s ^ (hv >> 23); 1126 ++s; 1127 } 1128 return(((hv >> 16) ^ hv) & HMASK); 1129 } 1130 1131 /* 1132 * WildCmp() - compare wild string to sane string 1133 * 1134 * Return 0 on success, -1 on failure. 1135 */ 1136 1137 int 1138 WildCmp(const char *w, const char *s) 1139 { 1140 /* 1141 * skip fixed portion 1142 */ 1143 1144 for (;;) { 1145 switch(*w) { 1146 case '*': 1147 if (w[1] == 0) /* optimize wild* case */ 1148 return(0); 1149 { 1150 int i; 1151 int l = strlen(s); 1152 1153 for (i = 0; i <= l; ++i) { 1154 if (WildCmp(w + 1, s + i) == 0) 1155 return(0); 1156 } 1157 } 1158 return(-1); 1159 case '?': 1160 if (*s == 0) 1161 return(-1); 1162 ++w; 1163 ++s; 1164 break; 1165 default: 1166 if (*w != *s) 1167 return(-1); 1168 if (*w == 0) /* terminator */ 1169 return(0); 1170 ++w; 1171 ++s; 1172 break; 1173 } 1174 } 1175 /* not reached */ 1176 return(-1); 1177 } 1178 1179 int 1180 YesNo(const char *path) 1181 { 1182 int ch, first; 1183 1184 fprintf(stderr, "remove %s (Yes/No) [No]? ", path); 1185 fflush(stderr); 1186 1187 first = ch = getchar(); 1188 while (ch != '\n' && ch != EOF) 1189 ch = getchar(); 1190 return ((first == 'y' || first == 'Y')); 1191 } 1192 1193 /* 1194 * xrename() - rename with override 1195 * 1196 * If the rename fails, attempt to override st_flags on the 1197 * destination and rename again. If that fails too, try to 1198 * set the flags back the way they were and give up. 1199 */ 1200 1201 static int 1202 xrename(const char *src, const char *dst, u_long flags) 1203 { 1204 int r; 1205 1206 r = 0; 1207 1208 if ((r = rename(src, dst)) < 0) { 1209 chflags(dst, 0); 1210 if ((r = rename(src, dst)) < 0) 1211 chflags(dst, flags); 1212 } 1213 return(r); 1214 } 1215 1216 static int 1217 xlink(const char *src, const char *dst, u_long flags) 1218 { 1219 int r, e; 1220 1221 r = 0; 1222 1223 if ((r = link(src, dst)) < 0) { 1224 chflags(src, 0); 1225 r = link(src, dst); 1226 e = errno; 1227 chflags(src, flags); 1228 errno = e; 1229 } 1230 return(r); 1231 } 1232 1233