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.26 2008/04/22 19:45:05 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 #include "hclink.h" 60 #include "hcproto.h" 61 62 #define HSIZE 8192 63 #define HMASK (HSIZE-1) 64 #define HLSIZE 8192 65 #define HLMASK (HLSIZE - 1) 66 67 #define MAXDEPTH 32 /* max copy depth for thread */ 68 #define GETBUFSIZE 8192 69 #define GETPATHSIZE 2048 70 #define GETLINKSIZE 1024 71 #define GETIOSIZE 65536 72 73 #ifndef _ST_FLAGS_PRESENT_ 74 #define st_flags st_mode 75 #endif 76 77 typedef struct Node { 78 struct Node *no_Next; 79 struct Node *no_HNext; 80 int no_Value; 81 char no_Name[4]; 82 } Node; 83 84 typedef struct List { 85 Node li_Node; 86 Node *li_Hash[HSIZE]; 87 } List; 88 89 struct hlink { 90 ino_t ino; 91 ino_t dino; 92 int refs; 93 struct hlink *next; 94 struct hlink *prev; 95 nlink_t nlinked; 96 char name[0]; 97 }; 98 99 typedef struct copy_info { 100 char *spath; 101 char *dpath; 102 dev_t sdevNo; 103 dev_t ddevNo; 104 #ifdef USE_PTHREADS 105 struct copy_info *parent; 106 pthread_cond_t cond; 107 int children; 108 int r; 109 #endif 110 } *copy_info_t; 111 112 struct hlink *hltable[HLSIZE]; 113 114 void RemoveRecur(const char *dpath, dev_t devNo); 115 void InitList(List *list); 116 void ResetList(List *list); 117 int AddList(List *list, const char *name, int n); 118 static struct hlink *hltlookup(struct stat *); 119 static struct hlink *hltadd(struct stat *, const char *); 120 static char *checkHLPath(struct stat *st, const char *spath, const char *dpath); 121 static int validate_check(const char *spath, const char *dpath); 122 static int shash(const char *s); 123 static void hltdelete(struct hlink *); 124 static void hltsetdino(struct hlink *, ino_t); 125 int YesNo(const char *path); 126 static int xrename(const char *src, const char *dst, u_long flags); 127 static int xlink(const char *src, const char *dst, u_long flags); 128 int WildCmp(const char *s1, const char *s2); 129 static int DoCopy(copy_info_t info, int depth); 130 131 int AskConfirmation = 1; 132 int SafetyOpt = 1; 133 int ForceOpt; 134 int DeviceOpt = 1; 135 int VerboseOpt; 136 int QuietOpt; 137 int NoRemoveOpt; 138 int UseMD5Opt; 139 int UseFSMIDOpt; 140 int SummaryOpt; 141 int CompressOpt; 142 int SlaveOpt; 143 int EnableDirectoryRetries; 144 int DstBaseLen; 145 int ValidateOpt; 146 int CurParallel; 147 int MaxParallel = -1; 148 int HardLinkCount; 149 const char *UseCpFile; 150 const char *UseHLPath; 151 const char *MD5CacheFile; 152 const char *FSMIDCacheFile; 153 154 int64_t CountSourceBytes; 155 int64_t CountSourceItems; 156 int64_t CountCopiedItems; 157 int64_t CountSourceReadBytes; 158 int64_t CountTargetReadBytes; 159 int64_t CountWriteBytes; 160 int64_t CountRemovedItems; 161 int64_t CountLinkedItems; 162 163 struct HostConf SrcHost; 164 struct HostConf DstHost; 165 166 #if USE_PTHREADS 167 pthread_mutex_t MasterMutex; 168 #endif 169 170 int 171 main(int ac, char **av) 172 { 173 int i; 174 char *src = NULL; 175 char *dst = NULL; 176 char *ptr; 177 struct timeval start; 178 struct copy_info info; 179 180 signal(SIGPIPE, SIG_IGN); 181 182 #if USE_PTHREADS 183 pthread_mutex_init(&SrcHost.read_mutex, NULL); 184 pthread_mutex_init(&DstHost.read_mutex, NULL); 185 pthread_mutex_init(&MasterMutex, NULL); 186 pthread_mutex_lock(&MasterMutex); 187 #endif 188 189 gettimeofday(&start, NULL); 190 for (i = 1; i < ac; ++i) { 191 int v = 1; 192 193 ptr = av[i]; 194 if (*ptr != '-') { 195 if (src == NULL) { 196 src = ptr; 197 } else if (dst == NULL) { 198 dst = ptr; 199 } else { 200 fatal("too many arguments"); 201 /* not reached */ 202 } 203 continue; 204 } 205 ptr += 2; 206 207 if (*ptr) 208 v = strtol(ptr, NULL, 0); 209 210 switch(ptr[-1]) { 211 case 'C': 212 CompressOpt = 1; 213 break; 214 case 'v': 215 VerboseOpt = 1; 216 while (*ptr == 'v') { 217 ++VerboseOpt; 218 ++ptr; 219 } 220 if (*ptr >= '0' && *ptr <= '9') 221 VerboseOpt = strtol(ptr, NULL, 0); 222 break; 223 case 'l': 224 setlinebuf(stdout); 225 setlinebuf(stderr); 226 break; 227 case 'V': 228 ValidateOpt = v; 229 break; 230 case 'I': 231 SummaryOpt = v; 232 break; 233 case 'o': 234 NoRemoveOpt = v; 235 break; 236 case 'x': 237 UseCpFile = ".cpignore"; 238 break; 239 case 'X': 240 UseCpFile = (*ptr) ? ptr : av[++i]; 241 break; 242 case 'H': 243 UseHLPath = (*ptr) ? ptr : av[++i]; 244 break; 245 case 'S': 246 SlaveOpt = v; 247 break; 248 case 'f': 249 ForceOpt = v; 250 break; 251 case 'i': 252 AskConfirmation = v; 253 break; 254 case 'j': 255 DeviceOpt = v; 256 break; 257 case 'p': 258 MaxParallel = v; 259 break; 260 case 's': 261 SafetyOpt = v; 262 break; 263 case 'q': 264 QuietOpt = v; 265 break; 266 case 'k': 267 UseFSMIDOpt = v; 268 FSMIDCacheFile = ".FSMID.CHECK"; 269 break; 270 case 'K': 271 UseFSMIDOpt = v; 272 FSMIDCacheFile = av[++i]; 273 break; 274 case 'M': 275 UseMD5Opt = v; 276 MD5CacheFile = av[++i]; 277 break; 278 case 'm': 279 UseMD5Opt = v; 280 MD5CacheFile = ".MD5.CHECKSUMS"; 281 break; 282 case 'u': 283 setvbuf(stdout, NULL, _IOLBF, 0); 284 break; 285 default: 286 fatal("illegal option: %s\n", ptr - 2); 287 /* not reached */ 288 break; 289 } 290 } 291 292 /* 293 * If we are told to go into slave mode, run the HC protocol 294 */ 295 if (SlaveOpt) { 296 hc_slave(0, 1); 297 exit(0); 298 } 299 300 /* 301 * Extract the source and/or/neither target [user@]host and 302 * make any required connections. 303 */ 304 if (src && (ptr = strchr(src, ':')) != NULL) { 305 asprintf(&SrcHost.host, "%*.*s", ptr - src, ptr - src, src); 306 src = ptr + 1; 307 if (UseCpFile) { 308 fprintf(stderr, "The cpignore options are not currently supported for remote sources\n"); 309 exit(1); 310 } 311 if (UseMD5Opt) { 312 fprintf(stderr, "The MD5 options are not currently supported for remote sources\n"); 313 exit(1); 314 } 315 if (hc_connect(&SrcHost) < 0) 316 exit(1); 317 } 318 if (dst && (ptr = strchr(dst, ':')) != NULL) { 319 asprintf(&DstHost.host, "%*.*s", ptr - dst, ptr - dst, dst); 320 dst = ptr + 1; 321 if (UseFSMIDOpt) { 322 fprintf(stderr, "The FSMID options are not currently supported for remote targets\n"); 323 exit(1); 324 } 325 if (hc_connect(&DstHost) < 0) 326 exit(1); 327 } 328 329 /* 330 * dst may be NULL only if -m option is specified, 331 * which forces an update of the MD5 checksums 332 */ 333 if (dst == NULL && UseMD5Opt == 0) { 334 fatal(NULL); 335 /* not reached */ 336 } 337 bzero(&info, sizeof(info)); 338 info.r = 0; 339 info.children = 0; 340 #if USE_PTHREADS 341 pthread_cond_init(&info.cond, NULL); 342 #endif 343 if (dst) { 344 DstBaseLen = strlen(dst); 345 info.spath = src; 346 info.dpath = dst; 347 info.sdevNo = (dev_t)-1; 348 info.ddevNo = (dev_t)-1; 349 i = DoCopy(&info, -1); 350 } else { 351 info.spath = src; 352 info.dpath = NULL; 353 info.sdevNo = (dev_t)-1; 354 info.ddevNo = (dev_t)-1; 355 i = DoCopy(&info, -1); 356 } 357 #if USE_PTHREADS 358 pthread_cond_destroy(&info.cond); 359 #endif 360 #ifndef NOMD5 361 md5_flush(); 362 #endif 363 fsmid_flush(); 364 365 if (SummaryOpt && i == 0) { 366 long duration; 367 struct timeval end; 368 369 gettimeofday(&end, NULL); 370 #if 0 371 /* don't count stat's in our byte statistics */ 372 CountSourceBytes += sizeof(struct stat) * CountSourceItems; 373 CountSourceReadBytes += sizeof(struct stat) * CountSourceItems; 374 CountWriteBytes += sizeof(struct stat) * CountCopiedItems; 375 CountWriteBytes += sizeof(struct stat) * CountRemovedItems; 376 #endif 377 378 duration = end.tv_sec - start.tv_sec; 379 duration *= 1000000; 380 duration += end.tv_usec - start.tv_usec; 381 if (duration == 0) duration = 1; 382 logstd("cpdup completed successfully\n"); 383 logstd("%lld bytes source, %lld src bytes read, %lld tgt bytes read\n" 384 "%lld bytes written (%.1fX speedup)\n", 385 (long long)CountSourceBytes, 386 (long long)CountSourceReadBytes, 387 (long long)CountTargetReadBytes, 388 (long long)CountWriteBytes, 389 ((double)CountSourceBytes * 2.0) / ((double)(CountSourceReadBytes + CountTargetReadBytes + CountWriteBytes))); 390 logstd("%lld source items, %lld items copied, %lld items linked, " 391 "%lld things deleted\n", 392 (long long)CountSourceItems, 393 (long long)CountCopiedItems, 394 (long long)CountLinkedItems, 395 (long long)CountRemovedItems); 396 logstd("%.1f seconds %5d Kbytes/sec synced %5d Kbytes/sec scanned\n", 397 (float)duration / (float)1000000, 398 (long)((long)1000000 * (CountSourceReadBytes + CountTargetReadBytes + CountWriteBytes) / duration / 1024.0), 399 (long)((long)1000000 * CountSourceBytes / duration / 1024.0)); 400 } 401 exit((i == 0) ? 0 : 1); 402 } 403 404 static struct hlink * 405 hltlookup(struct stat *stp) 406 { 407 struct timespec ts = { 0, 100000 }; 408 struct hlink *hl; 409 int n; 410 411 n = stp->st_ino & HLMASK; 412 413 #if USE_PTHREADS 414 again: 415 #endif 416 for (hl = hltable[n]; hl; hl = hl->next) { 417 if (hl->ino == stp->st_ino) { 418 #if USE_PTHREADS 419 /* 420 * If the hl entry is still in the process of being created 421 * by another thread we have to wait until it has either been 422 * deleted or completed. 423 */ 424 if (hl->refs) { 425 pthread_mutex_unlock(&MasterMutex); 426 nanosleep(&ts, NULL); 427 pthread_mutex_lock(&MasterMutex); 428 goto again; 429 } 430 #endif 431 ++hl->refs; 432 return hl; 433 } 434 } 435 436 return NULL; 437 } 438 439 static struct hlink * 440 hltadd(struct stat *stp, const char *path) 441 { 442 struct hlink *new; 443 int plen = strlen(path); 444 int n; 445 446 new = malloc(offsetof(struct hlink, name[plen + 1])); 447 if (new == NULL) { 448 fprintf(stderr, "out of memory\n"); 449 exit(EXIT_FAILURE); 450 } 451 ++HardLinkCount; 452 453 /* initialize and link the new element into the table */ 454 new->ino = stp->st_ino; 455 new->dino = (ino_t)-1; 456 new->refs = 1; 457 bcopy(path, new->name, plen + 1); 458 new->nlinked = 1; 459 new->prev = NULL; 460 n = stp->st_ino & HLMASK; 461 new->next = hltable[n]; 462 if (hltable[n]) 463 hltable[n]->prev = new; 464 hltable[n] = new; 465 466 return new; 467 } 468 469 static void 470 hltsetdino(struct hlink *hl, ino_t inum) 471 { 472 hl->dino = inum; 473 } 474 475 static void 476 hltdelete(struct hlink *hl) 477 { 478 assert(hl->refs == 1); 479 --hl->refs; 480 if (hl->prev) { 481 if (hl->next) 482 hl->next->prev = hl->prev; 483 hl->prev->next = hl->next; 484 } else { 485 if (hl->next) 486 hl->next->prev = NULL; 487 488 hltable[hl->ino & HLMASK] = hl->next; 489 } 490 --HardLinkCount; 491 free(hl); 492 } 493 494 static void 495 hltrels(struct hlink *hl) 496 { 497 assert(hl->refs == 1); 498 --hl->refs; 499 } 500 501 /* 502 * If UseHLPath is defined check to see if the file in question is 503 * the same as the source file, and if it is return a pointer to the 504 * -H path based file for hardlinking. Else return NULL. 505 */ 506 static char * 507 checkHLPath(struct stat *st1, const char *spath, const char *dpath) 508 { 509 struct stat sthl; 510 char *hpath; 511 int error; 512 513 asprintf(&hpath, "%s%s", UseHLPath, dpath + DstBaseLen); 514 515 /* 516 * stat info matches ? 517 */ 518 if (hc_stat(&DstHost, hpath, &sthl) < 0 || 519 st1->st_size != sthl.st_size || 520 st1->st_uid != sthl.st_uid || 521 st1->st_gid != sthl.st_gid || 522 st1->st_mtime != sthl.st_mtime 523 ) { 524 free(hpath); 525 return(NULL); 526 } 527 528 /* 529 * If ForceOpt or ValidateOpt is set we have to compare the files 530 */ 531 if (ForceOpt || ValidateOpt) { 532 error = validate_check(spath, hpath); 533 if (error) { 534 free(hpath); 535 hpath = NULL; 536 } 537 } 538 return(hpath); 539 } 540 541 /* 542 * Return 0 if the contents of the file <spath> matches the contents of 543 * the file <dpath>. 544 */ 545 static int 546 validate_check(const char *spath, const char *dpath) 547 { 548 int error; 549 int fd1; 550 int fd2; 551 552 fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0); 553 fd2 = hc_open(&DstHost, dpath, O_RDONLY, 0); 554 error = -1; 555 556 if (fd1 >= 0 && fd2 >= 0) { 557 int n; 558 int x; 559 char *iobuf1 = malloc(GETIOSIZE); 560 char *iobuf2 = malloc(GETIOSIZE); 561 562 while ((n = hc_read(&SrcHost, fd1, iobuf1, GETIOSIZE)) > 0) { 563 CountSourceReadBytes += n; 564 x = hc_read(&DstHost, fd2, iobuf2, GETIOSIZE); 565 if (x > 0) 566 CountTargetReadBytes += x; 567 if (x != n) 568 break; 569 if (bcmp(iobuf1, iobuf2, n) != 0) 570 break; 571 } 572 free(iobuf1); 573 free(iobuf2); 574 if (n == 0) 575 error = 0; 576 } 577 if (fd1 >= 0) 578 hc_close(&SrcHost, fd1); 579 if (fd2 >= 0) 580 hc_close(&DstHost, fd2); 581 return (error); 582 } 583 #if USE_PTHREADS 584 585 static void * 586 DoCopyThread(void *arg) 587 { 588 copy_info_t cinfo = arg; 589 char *spath = cinfo->spath; 590 char *dpath = cinfo->dpath; 591 int r; 592 593 r = pthread_detach(pthread_self()); 594 assert(r == 0); 595 pthread_cond_init(&cinfo->cond, NULL); 596 pthread_mutex_lock(&MasterMutex); 597 cinfo->r += DoCopy(cinfo, 0); 598 /* cinfo arguments invalid on return */ 599 --cinfo->parent->children; 600 --CurParallel; 601 pthread_cond_signal(&cinfo->parent->cond); 602 free(spath); 603 if (dpath) 604 free(dpath); 605 pthread_cond_destroy(&cinfo->cond); 606 free(cinfo); 607 hcc_free_trans(&SrcHost); 608 hcc_free_trans(&DstHost); 609 pthread_mutex_unlock(&MasterMutex); 610 return(NULL); 611 } 612 613 #endif 614 615 int 616 DoCopy(copy_info_t info, int depth) 617 { 618 const char *spath = info->spath; 619 const char *dpath = info->dpath; 620 dev_t sdevNo = info->sdevNo; 621 dev_t ddevNo = info->ddevNo; 622 struct stat st1; 623 struct stat st2; 624 int r, mres, fres, st2Valid; 625 struct hlink *hln; 626 List *list = malloc(sizeof(List)); 627 u_int64_t size; 628 629 InitList(list); 630 r = mres = fres = st2Valid = 0; 631 size = 0; 632 hln = NULL; 633 634 if (hc_lstat(&SrcHost, spath, &st1) != 0) { 635 r = 0; 636 goto done; 637 } 638 st2.st_mode = 0; /* in case lstat fails */ 639 st2.st_flags = 0; /* in case lstat fails */ 640 if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0) 641 st2Valid = 1; 642 643 if (S_ISREG(st1.st_mode)) { 644 size = st1.st_size; 645 } 646 647 /* 648 * Handle hardlinks 649 */ 650 651 if (S_ISREG(st1.st_mode) && st1.st_nlink > 1 && dpath) { 652 if ((hln = hltlookup(&st1)) != NULL) { 653 hln->nlinked++; 654 655 if (st2Valid) { 656 if (st2.st_ino == hln->dino) { 657 /* 658 * hard link is already correct, nothing to do 659 */ 660 if (VerboseOpt >= 3) 661 logstd("%-32s nochange\n", (dpath) ? dpath : spath); 662 if (hln->nlinked == st1.st_nlink) { 663 hltdelete(hln); 664 hln = NULL; 665 } 666 CountSourceItems++; 667 r = 0; 668 goto done; 669 } else { 670 /* 671 * hard link is not correct, attempt to unlink it 672 */ 673 if (hc_remove(&DstHost, dpath) < 0) { 674 logerr("%-32s hardlink: unable to unlink: %s\n", 675 ((dpath) ? dpath : spath), strerror(errno)); 676 hltdelete(hln); 677 hln = NULL; 678 ++r; 679 goto done; 680 } 681 } 682 } 683 684 if (xlink(hln->name, dpath, st1.st_flags) < 0) { 685 int tryrelink = (errno == EMLINK); 686 logerr("%-32s hardlink: unable to link to %s: %s\n", 687 (dpath ? dpath : spath), hln->name, strerror(errno) 688 ); 689 hltdelete(hln); 690 hln = NULL; 691 if (tryrelink) { 692 logerr("%-20s hardlink: will attempt to copy normally\n"); 693 goto relink; 694 } 695 ++r; 696 } else { 697 if (hln->nlinked == st1.st_nlink) { 698 hltdelete(hln); 699 hln = NULL; 700 } 701 if (r == 0) { 702 if (VerboseOpt) { 703 logstd("%-32s hardlink: %s\n", 704 (dpath ? dpath : spath), 705 (st2Valid ? "relinked" : "linked") 706 ); 707 } 708 CountSourceItems++; 709 CountCopiedItems++; 710 r = 0; 711 goto done; 712 } 713 } 714 } else { 715 /* 716 * first instance of hardlink must be copied normally 717 */ 718 relink: 719 hln = hltadd(&st1, dpath); 720 } 721 } 722 723 /* 724 * Do we need to copy the file/dir/link/whatever? Early termination 725 * if we do not. Always redo links. Directories are always traversed 726 * except when the FSMID options are used. 727 * 728 * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good. 729 */ 730 731 if ( 732 st2Valid 733 && st1.st_mode == st2.st_mode 734 #ifdef _ST_FLAGS_PRESENT_ 735 && st1.st_flags == st2.st_flags 736 #endif 737 ) { 738 if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) { 739 /* 740 * If FSMID tracking is turned on we can avoid recursing through 741 * an entire directory subtree if the FSMID matches. 742 */ 743 #ifdef _ST_FSMID_PRESENT_ 744 if (ForceOpt == 0 && 745 (UseFSMIDOpt && (fres = fsmid_check(st1.st_fsmid, dpath)) == 0) 746 ) { 747 if (VerboseOpt >= 3) { 748 if (UseFSMIDOpt) 749 logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath)); 750 else 751 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 752 } 753 r = 0; 754 goto done; 755 } 756 #endif 757 } else { 758 if (ForceOpt == 0 && 759 st1.st_size == st2.st_size && 760 st1.st_uid == st2.st_uid && 761 st1.st_gid == st2.st_gid && 762 st1.st_mtime == st2.st_mtime 763 #ifndef NOMD5 764 && (UseMD5Opt == 0 || !S_ISREG(st1.st_mode) || 765 (mres = md5_check(spath, dpath)) == 0) 766 #endif 767 #ifdef _ST_FSMID_PRESENT_ 768 && (UseFSMIDOpt == 0 || 769 (fres = fsmid_check(st1.st_fsmid, dpath)) == 0) 770 #endif 771 && (ValidateOpt == 0 || !S_ISREG(st1.st_mode) || 772 validate_check(spath, dpath) == 0) 773 ) { 774 if (hln) 775 hltsetdino(hln, st2.st_ino); 776 if (VerboseOpt >= 3) { 777 #ifndef NOMD5 778 if (UseMD5Opt) 779 logstd("%-32s md5-nochange\n", (dpath ? dpath : spath)); 780 else 781 #endif 782 if (UseFSMIDOpt) 783 logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath)); 784 else if (ValidateOpt) 785 logstd("%-32s nochange (contents validated)\n", (dpath ? dpath : spath)); 786 else 787 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 788 } 789 CountSourceBytes += size; 790 CountSourceItems++; 791 r = 0; 792 goto done; 793 } 794 } 795 } 796 if (st2Valid && !S_ISDIR(st1.st_mode) && S_ISDIR(st2.st_mode)) { 797 if (SafetyOpt) { 798 logerr("%-32s SAFETY - refusing to copy file over directory\n", 799 (dpath ? dpath : spath) 800 ); 801 ++r; /* XXX */ 802 r = 0; 803 goto done; /* continue with the cpdup anyway */ 804 } 805 if (QuietOpt == 0 || AskConfirmation) { 806 logstd("%-32s WARNING: non-directory source will blow away\n" 807 "%-32s preexisting dest directory, continuing anyway!\n", 808 ((dpath) ? dpath : spath), ""); 809 } 810 if (dpath) 811 RemoveRecur(dpath, ddevNo); 812 } 813 814 /* 815 * The various comparisons failed, copy it. 816 */ 817 if (S_ISDIR(st1.st_mode)) { 818 DIR *dir; 819 820 if (fres < 0) 821 logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath); 822 if ((dir = hc_opendir(&SrcHost, spath)) != NULL) { 823 struct dirent *den; 824 int noLoop = 0; 825 826 if (dpath) { 827 if (S_ISDIR(st2.st_mode) == 0) { 828 hc_remove(&DstHost, dpath); 829 if (hc_mkdir(&DstHost, dpath, st1.st_mode | 0700) != 0) { 830 logerr("%s: mkdir failed: %s\n", 831 (dpath ? dpath : spath), strerror(errno)); 832 r = 1; 833 noLoop = 1; 834 } 835 /* 836 * Matt: why don't you check error codes here? 837 */ 838 hc_lstat(&DstHost, dpath, &st2); 839 hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid); 840 CountCopiedItems++; 841 } else { 842 /* 843 * Directory must be scanable by root for cpdup to 844 * work. We'll fix it later if the directory isn't 845 * supposed to be readable ( which is why we fixup 846 * st2.st_mode to match what we did ). 847 */ 848 if ((st2.st_mode & 0700) != 0700) { 849 hc_chmod(&DstHost, dpath, st2.st_mode | 0700); 850 st2.st_mode |= 0700; 851 } 852 if (VerboseOpt >= 2) 853 logstd("%s\n", dpath ? dpath : spath); 854 } 855 } 856 857 if ((int)sdevNo >= 0 && st1.st_dev != sdevNo) { 858 noLoop = 1; 859 } else { 860 sdevNo = st1.st_dev; 861 } 862 863 if ((int)ddevNo >= 0 && st2.st_dev != ddevNo) { 864 noLoop = 1; 865 } else { 866 ddevNo = st2.st_dev; 867 } 868 869 /* 870 * scan .cpignore file for files/directories 871 * to ignore. 872 */ 873 874 if (UseCpFile) { 875 FILE *fi; 876 char *buf = malloc(GETBUFSIZE); 877 char *fpath; 878 879 if (UseCpFile[0] == '/') { 880 fpath = mprintf("%s", UseCpFile); 881 } else { 882 fpath = mprintf("%s/%s", spath, UseCpFile); 883 } 884 AddList(list, strrchr(fpath, '/') + 1, 1); 885 if ((fi = fopen(fpath, "r")) != NULL) { 886 while (fgets(buf, GETBUFSIZE, fi) != NULL) { 887 int l = strlen(buf); 888 CountSourceReadBytes += l; 889 if (l && buf[l-1] == '\n') 890 buf[--l] = 0; 891 if (buf[0] && buf[0] != '#') 892 AddList(list, buf, 1); 893 } 894 fclose(fi); 895 } 896 free(fpath); 897 free(buf); 898 } 899 900 /* 901 * Automatically exclude MD5CacheFile that we create on the 902 * source from the copy to the destination. 903 * 904 * Automatically exclude a FSMIDCacheFile on the source that 905 * would otherwise overwrite the one we maintain on the target. 906 */ 907 if (UseMD5Opt) 908 AddList(list, MD5CacheFile, 1); 909 if (UseFSMIDOpt) 910 AddList(list, FSMIDCacheFile, 1); 911 912 while (noLoop == 0 && (den = hc_readdir(&SrcHost, dir)) != NULL) { 913 /* 914 * ignore . and .. 915 */ 916 char *nspath; 917 char *ndpath = NULL; 918 919 if (strcmp(den->d_name, ".") == 0 || 920 strcmp(den->d_name, "..") == 0 921 ) { 922 continue; 923 } 924 /* 925 * ignore if on .cpignore list 926 */ 927 if (AddList(list, den->d_name, 0) == 1) { 928 continue; 929 } 930 nspath = mprintf("%s/%s", spath, den->d_name); 931 if (dpath) 932 ndpath = mprintf("%s/%s", dpath, den->d_name); 933 934 #if USE_PTHREADS 935 if (CurParallel < MaxParallel || depth > MAXDEPTH) { 936 copy_info_t cinfo = malloc(sizeof(*cinfo)); 937 pthread_t dummy_thr = NULL; 938 939 bzero(cinfo, sizeof(*cinfo)); 940 cinfo->spath = nspath; 941 cinfo->dpath = ndpath; 942 cinfo->sdevNo = sdevNo; 943 cinfo->ddevNo = ddevNo; 944 cinfo->parent = info; 945 ++CurParallel; 946 ++info->children; 947 pthread_create(&dummy_thr, NULL, DoCopyThread, cinfo); 948 } else 949 #endif 950 { 951 info->spath = nspath; 952 info->dpath = ndpath; 953 info->sdevNo = sdevNo; 954 info->ddevNo = ddevNo; 955 if (depth < 0) 956 r += DoCopy(info, depth); 957 else 958 r += DoCopy(info, depth + 1); 959 free(nspath); 960 if (ndpath) 961 free(ndpath); 962 info->spath = NULL; 963 info->dpath = NULL; 964 } 965 } 966 967 hc_closedir(&SrcHost, dir); 968 969 #if USE_PTHREADS 970 /* 971 * Wait for our children to finish 972 */ 973 while (info->children) { 974 pthread_cond_wait(&info->cond, &MasterMutex); 975 } 976 r += info->r; 977 info->r = 0; 978 #endif 979 980 /* 981 * Remove files/directories from destination that do not appear 982 * in the source. 983 */ 984 if (dpath && (dir = hc_opendir(&DstHost, dpath)) != NULL) { 985 while (noLoop == 0 && (den = hc_readdir(&DstHost, dir)) != NULL) { 986 /* 987 * ignore . or .. 988 */ 989 if (strcmp(den->d_name, ".") == 0 || 990 strcmp(den->d_name, "..") == 0 991 ) { 992 continue; 993 } 994 /* 995 * If object does not exist in source or .cpignore 996 * then recursively remove it. 997 */ 998 if (AddList(list, den->d_name, 3) == 3) { 999 char *ndpath; 1000 1001 ndpath = mprintf("%s/%s", dpath, den->d_name); 1002 RemoveRecur(ndpath, ddevNo); 1003 free(ndpath); 1004 } 1005 } 1006 hc_closedir(&DstHost, dir); 1007 } 1008 1009 if (dpath) { 1010 struct timeval tv[2]; 1011 1012 if (ForceOpt || 1013 st2Valid == 0 || 1014 st1.st_uid != st2.st_uid || 1015 st1.st_gid != st2.st_gid 1016 ) { 1017 hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid); 1018 } 1019 if (st2Valid == 0 || st1.st_mode != st2.st_mode) { 1020 hc_chmod(&DstHost, dpath, st1.st_mode); 1021 } 1022 #ifdef _ST_FLAGS_PRESENT_ 1023 if (st2Valid == 0 || st1.st_flags != st2.st_flags) { 1024 hc_chflags(&DstHost, dpath, st1.st_flags); 1025 } 1026 #endif 1027 if (ForceOpt || 1028 st2Valid == 0 || 1029 st1.st_mtime != st2.st_mtime 1030 ) { 1031 bzero(tv, sizeof(tv)); 1032 tv[0].tv_sec = st1.st_mtime; 1033 tv[1].tv_sec = st1.st_mtime; 1034 hc_utimes(&DstHost, dpath, tv); 1035 } 1036 } 1037 } 1038 } else if (dpath == NULL) { 1039 /* 1040 * If dpath is NULL, we are just updating the MD5 1041 */ 1042 #ifndef NOMD5 1043 if (UseMD5Opt && S_ISREG(st1.st_mode)) { 1044 mres = md5_check(spath, NULL); 1045 1046 if (VerboseOpt > 1) { 1047 if (mres < 0) 1048 logstd("%-32s md5-update\n", (dpath) ? dpath : spath); 1049 else 1050 logstd("%-32s md5-ok\n", (dpath) ? dpath : spath); 1051 } else if (!QuietOpt && mres < 0) { 1052 logstd("%-32s md5-update\n", (dpath) ? dpath : spath); 1053 } 1054 } 1055 #endif 1056 } else if (S_ISREG(st1.st_mode)) { 1057 char *path; 1058 char *hpath; 1059 int fd1; 1060 int fd2; 1061 1062 path = mprintf("%s.tmp%d", dpath, (int)getpid()); 1063 1064 /* 1065 * Handle check failure message. 1066 */ 1067 #ifndef NOMD5 1068 if (mres < 0) 1069 logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath); 1070 else 1071 #endif 1072 if (fres < 0) 1073 logerr("%-32s fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath); 1074 1075 /* 1076 * Not quite ready to do the copy yet. If UseHLPath is defined, 1077 * see if we can hardlink instead. 1078 * 1079 * If we can hardlink, and the target exists, we have to remove it 1080 * first or the hardlink will fail. This can occur in a number of 1081 * situations but must typically when the '-f -H' combination is 1082 * used. 1083 */ 1084 if (UseHLPath && (hpath = checkHLPath(&st1, spath, dpath)) != NULL) { 1085 if (st2Valid) 1086 hc_remove(&DstHost, dpath); 1087 if (hc_link(&DstHost, hpath, dpath) == 0) { 1088 ++CountLinkedItems; 1089 if (VerboseOpt) { 1090 logstd("%-32s hardlinked(-H)\n", 1091 (dpath ? dpath : spath)); 1092 } 1093 free(hpath); 1094 goto skip_copy; 1095 } 1096 /* 1097 * Shucks, we may have hit a filesystem hard linking limit, 1098 * we have to copy instead. 1099 */ 1100 free(hpath); 1101 } 1102 1103 if ((fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0)) >= 0) { 1104 if ((fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) { 1105 /* 1106 * There could be a .tmp file from a previously interrupted 1107 * run, delete and retry. Fail if we still can't get at it. 1108 */ 1109 #ifdef _ST_FLAGS_PRESENT_ 1110 hc_chflags(&DstHost, path, 0); 1111 #endif 1112 hc_remove(&DstHost, path); 1113 fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600); 1114 } 1115 if (fd2 >= 0) { 1116 const char *op; 1117 char *iobuf1 = malloc(GETIOSIZE); 1118 int n; 1119 1120 /* 1121 * Matt: What about holes? 1122 */ 1123 op = "read"; 1124 while ((n = hc_read(&SrcHost, fd1, iobuf1, GETIOSIZE)) > 0) { 1125 op = "write"; 1126 if (hc_write(&DstHost, fd2, iobuf1, n) != n) 1127 break; 1128 op = "read"; 1129 } 1130 hc_close(&DstHost, fd2); 1131 if (n == 0) { 1132 struct timeval tv[2]; 1133 1134 bzero(tv, sizeof(tv)); 1135 tv[0].tv_sec = st1.st_mtime; 1136 tv[1].tv_sec = st1.st_mtime; 1137 1138 hc_utimes(&DstHost, path, tv); 1139 hc_chown(&DstHost, path, st1.st_uid, st1.st_gid); 1140 hc_chmod(&DstHost, path, st1.st_mode); 1141 if (xrename(path, dpath, st2.st_flags) != 0) { 1142 logerr("%-32s rename-after-copy failed: %s\n", 1143 (dpath ? dpath : spath), strerror(errno) 1144 ); 1145 ++r; 1146 } else { 1147 if (VerboseOpt) 1148 logstd("%-32s copy-ok\n", (dpath ? dpath : spath)); 1149 #ifdef _ST_FLAGS_PRESENT_ 1150 if (st1.st_flags) 1151 hc_chflags(&DstHost, dpath, st1.st_flags); 1152 #endif 1153 } 1154 CountSourceReadBytes += size; 1155 CountWriteBytes += size; 1156 CountSourceBytes += size; 1157 CountSourceItems++; 1158 CountCopiedItems++; 1159 } else { 1160 logerr("%-32s %s failed: %s\n", 1161 (dpath ? dpath : spath), op, strerror(errno) 1162 ); 1163 hc_remove(&DstHost, path); 1164 ++r; 1165 } 1166 free(iobuf1); 1167 } else { 1168 logerr("%-32s create (uid %d, euid %d) failed: %s\n", 1169 (dpath ? dpath : spath), getuid(), geteuid(), 1170 strerror(errno) 1171 ); 1172 ++r; 1173 } 1174 hc_close(&SrcHost, fd1); 1175 } else { 1176 logerr("%-32s copy: open failed: %s\n", 1177 (dpath ? dpath : spath), 1178 strerror(errno) 1179 ); 1180 ++r; 1181 } 1182 skip_copy: 1183 free(path); 1184 1185 if (hln) { 1186 if (!r && hc_stat(&DstHost, dpath, &st2) == 0) { 1187 hltsetdino(hln, st2.st_ino); 1188 } else { 1189 hltdelete(hln); 1190 hln = NULL; 1191 } 1192 } 1193 } else if (S_ISLNK(st1.st_mode)) { 1194 char *link1 = malloc(GETLINKSIZE); 1195 char *link2 = malloc(GETLINKSIZE); 1196 char *path = malloc(GETPATHSIZE); 1197 int n1; 1198 int n2; 1199 1200 snprintf(path, GETPATHSIZE, "%s.tmp%d", dpath, (int)getpid()); 1201 n1 = hc_readlink(&SrcHost, spath, link1, GETLINKSIZE - 1); 1202 n2 = hc_readlink(&DstHost, dpath, link2, GETLINKSIZE - 1); 1203 if (n1 >= 0) { 1204 if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) { 1205 hc_umask(&DstHost, ~st1.st_mode); 1206 hc_remove(&DstHost, path); 1207 link1[n1] = 0; 1208 if (hc_symlink(&DstHost, link1, path) < 0) { 1209 logerr("%-32s symlink (%s->%s) failed: %s\n", 1210 (dpath ? dpath : spath), link1, path, 1211 strerror(errno) 1212 ); 1213 ++r; 1214 } else { 1215 hc_lchown(&DstHost, path, st1.st_uid, st1.st_gid); 1216 /* 1217 * there is no lchmod() or lchflags(), we 1218 * cannot chmod or chflags a softlink. 1219 */ 1220 if (xrename(path, dpath, st2.st_flags) != 0) { 1221 logerr("%-32s rename softlink (%s->%s) failed: %s\n", 1222 (dpath ? dpath : spath), 1223 path, dpath, strerror(errno)); 1224 } else if (VerboseOpt) { 1225 logstd("%-32s softlink-ok\n", (dpath ? dpath : spath)); 1226 } 1227 hc_umask(&DstHost, 000); 1228 CountWriteBytes += n1; 1229 CountCopiedItems++; 1230 } 1231 } else { 1232 if (VerboseOpt >= 3) 1233 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 1234 } 1235 CountSourceBytes += n1; 1236 CountSourceReadBytes += n1; 1237 if (n2 > 0) 1238 CountTargetReadBytes += n2; 1239 CountSourceItems++; 1240 } else { 1241 r = 1; 1242 logerr("%-32s softlink-failed\n", (dpath ? dpath : spath)); 1243 } 1244 free(link1); 1245 free(link2); 1246 free(path); 1247 } else if ((S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) && DeviceOpt) { 1248 char *path = malloc(GETPATHSIZE); 1249 1250 if (ForceOpt || 1251 st2Valid == 0 || 1252 st1.st_mode != st2.st_mode || 1253 st1.st_rdev != st2.st_rdev || 1254 st1.st_uid != st2.st_uid || 1255 st1.st_gid != st2.st_gid 1256 ) { 1257 snprintf(path, GETPATHSIZE, "%s.tmp%d", dpath, (int)getpid()); 1258 1259 hc_remove(&DstHost, path); 1260 if (hc_mknod(&DstHost, path, st1.st_mode, st1.st_rdev) == 0) { 1261 hc_chmod(&DstHost, path, st1.st_mode); 1262 hc_chown(&DstHost, path, st1.st_uid, st1.st_gid); 1263 hc_remove(&DstHost, dpath); 1264 if (xrename(path, dpath, st2.st_flags) != 0) { 1265 logerr("%-32s dev-rename-after-create failed: %s\n", 1266 (dpath ? dpath : spath), 1267 strerror(errno) 1268 ); 1269 } else if (VerboseOpt) { 1270 logstd("%-32s dev-ok\n", (dpath ? dpath : spath)); 1271 } 1272 CountCopiedItems++; 1273 } else { 1274 r = 1; 1275 logerr("%-32s dev failed: %s\n", 1276 (dpath ? dpath : spath), strerror(errno) 1277 ); 1278 } 1279 } else { 1280 if (VerboseOpt >= 3) 1281 logstd("%-32s nochange\n", (dpath ? dpath : spath)); 1282 } 1283 free(path); 1284 CountSourceItems++; 1285 } 1286 done: 1287 if (hln) { 1288 if (hln->dino == (ino_t)-1) { 1289 hltdelete(hln); 1290 /*hln = NULL; unneeded */ 1291 } else { 1292 hltrels(hln); 1293 } 1294 } 1295 ResetList(list); 1296 free(list); 1297 return (r); 1298 } 1299 1300 /* 1301 * RemoveRecur() 1302 */ 1303 1304 void 1305 RemoveRecur(const char *dpath, dev_t devNo) 1306 { 1307 struct stat st; 1308 1309 if (hc_lstat(&DstHost, dpath, &st) == 0) { 1310 if ((int)devNo < 0) 1311 devNo = st.st_dev; 1312 if (st.st_dev == devNo) { 1313 if (S_ISDIR(st.st_mode)) { 1314 DIR *dir; 1315 1316 if ((dir = hc_opendir(&DstHost, dpath)) != NULL) { 1317 struct dirent *den; 1318 while ((den = hc_readdir(&DstHost, dir)) != NULL) { 1319 char *ndpath; 1320 1321 if (strcmp(den->d_name, ".") == 0) 1322 continue; 1323 if (strcmp(den->d_name, "..") == 0) 1324 continue; 1325 ndpath = mprintf("%s/%s", dpath, den->d_name); 1326 RemoveRecur(ndpath, devNo); 1327 free(ndpath); 1328 } 1329 hc_closedir(&DstHost, dir); 1330 } 1331 if (AskConfirmation && NoRemoveOpt == 0) { 1332 if (YesNo(dpath)) { 1333 if (hc_rmdir(&DstHost, dpath) < 0) { 1334 logerr("%-32s rmdir failed: %s\n", 1335 dpath, strerror(errno) 1336 ); 1337 } 1338 CountRemovedItems++; 1339 } 1340 } else { 1341 if (NoRemoveOpt) { 1342 if (VerboseOpt) 1343 logstd("%-32s not-removed\n", dpath); 1344 } else if (hc_rmdir(&DstHost, dpath) == 0) { 1345 if (VerboseOpt) 1346 logstd("%-32s rmdir-ok\n", dpath); 1347 CountRemovedItems++; 1348 } else { 1349 logerr("%-32s rmdir failed: %s\n", 1350 dpath, strerror(errno) 1351 ); 1352 } 1353 } 1354 } else { 1355 if (AskConfirmation && NoRemoveOpt == 0) { 1356 if (YesNo(dpath)) { 1357 if (hc_remove(&DstHost, dpath) < 0) { 1358 logerr("%-32s remove failed: %s\n", 1359 dpath, strerror(errno) 1360 ); 1361 } 1362 CountRemovedItems++; 1363 } 1364 } else { 1365 if (NoRemoveOpt) { 1366 if (VerboseOpt) 1367 logstd("%-32s not-removed\n", dpath); 1368 } else if (hc_remove(&DstHost, dpath) == 0) { 1369 if (VerboseOpt) 1370 logstd("%-32s remove-ok\n", dpath); 1371 CountRemovedItems++; 1372 } else { 1373 logerr("%-32s remove failed: %s\n", 1374 dpath, strerror(errno) 1375 ); 1376 } 1377 } 1378 } 1379 } 1380 } 1381 } 1382 1383 void 1384 InitList(List *list) 1385 { 1386 bzero(list, sizeof(List)); 1387 list->li_Node.no_Next = &list->li_Node; 1388 } 1389 1390 void 1391 ResetList(List *list) 1392 { 1393 Node *node; 1394 1395 while ((node = list->li_Node.no_Next) != &list->li_Node) { 1396 list->li_Node.no_Next = node->no_Next; 1397 free(node); 1398 } 1399 InitList(list); 1400 } 1401 1402 int 1403 AddList(List *list, const char *name, int n) 1404 { 1405 Node *node; 1406 int hv; 1407 1408 hv = shash(name); 1409 1410 /* 1411 * Scan against wildcards. Only a node value of 1 can be a wildcard 1412 * ( usually scanned from .cpignore ) 1413 */ 1414 1415 for (node = list->li_Hash[0]; node; node = node->no_HNext) { 1416 if (strcmp(name, node->no_Name) == 0 || 1417 (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0) 1418 ) { 1419 return(node->no_Value); 1420 } 1421 } 1422 1423 /* 1424 * Look for exact match 1425 */ 1426 1427 for (node = list->li_Hash[hv]; node; node = node->no_HNext) { 1428 if (strcmp(name, node->no_Name) == 0) { 1429 return(node->no_Value); 1430 } 1431 } 1432 node = malloc(sizeof(Node) + strlen(name) + 1); 1433 if (node == NULL) { 1434 fprintf(stderr, "out of memory\n"); 1435 exit(EXIT_FAILURE); 1436 } 1437 1438 node->no_Next = list->li_Node.no_Next; 1439 list->li_Node.no_Next = node; 1440 1441 node->no_HNext = list->li_Hash[hv]; 1442 list->li_Hash[hv] = node; 1443 1444 strcpy(node->no_Name, name); 1445 node->no_Value = n; 1446 1447 return(n); 1448 } 1449 1450 static int 1451 shash(const char *s) 1452 { 1453 int hv; 1454 1455 hv = 0xA4FB3255; 1456 1457 while (*s) { 1458 if (*s == '*' || *s == '?' || 1459 *s == '{' || *s == '}' || 1460 *s == '[' || *s == ']' || 1461 *s == '|' 1462 ) { 1463 return(0); 1464 } 1465 hv = (hv << 5) ^ *s ^ (hv >> 23); 1466 ++s; 1467 } 1468 return(((hv >> 16) ^ hv) & HMASK); 1469 } 1470 1471 /* 1472 * WildCmp() - compare wild string to sane string 1473 * 1474 * Return 0 on success, -1 on failure. 1475 */ 1476 1477 int 1478 WildCmp(const char *w, const char *s) 1479 { 1480 /* 1481 * skip fixed portion 1482 */ 1483 1484 for (;;) { 1485 switch(*w) { 1486 case '*': 1487 if (w[1] == 0) /* optimize wild* case */ 1488 return(0); 1489 { 1490 int i; 1491 int l = strlen(s); 1492 1493 for (i = 0; i <= l; ++i) { 1494 if (WildCmp(w + 1, s + i) == 0) 1495 return(0); 1496 } 1497 } 1498 return(-1); 1499 case '?': 1500 if (*s == 0) 1501 return(-1); 1502 ++w; 1503 ++s; 1504 break; 1505 default: 1506 if (*w != *s) 1507 return(-1); 1508 if (*w == 0) /* terminator */ 1509 return(0); 1510 ++w; 1511 ++s; 1512 break; 1513 } 1514 } 1515 /* not reached */ 1516 return(-1); 1517 } 1518 1519 int 1520 YesNo(const char *path) 1521 { 1522 int ch, first; 1523 1524 fprintf(stderr, "remove %s (Yes/No) [No]? ", path); 1525 fflush(stderr); 1526 1527 first = ch = getchar(); 1528 while (ch != '\n' && ch != EOF) 1529 ch = getchar(); 1530 return ((first == 'y' || first == 'Y')); 1531 } 1532 1533 /* 1534 * xrename() - rename with override 1535 * 1536 * If the rename fails, attempt to override st_flags on the 1537 * destination and rename again. If that fails too, try to 1538 * set the flags back the way they were and give up. 1539 */ 1540 1541 static int 1542 xrename(const char *src, const char *dst, u_long flags) 1543 { 1544 int r; 1545 1546 r = 0; 1547 1548 if ((r = hc_rename(&DstHost, src, dst)) < 0) { 1549 #ifdef _ST_FLAGS_PRESENT_ 1550 hc_chflags(&DstHost, dst, 0); 1551 if ((r = hc_rename(&DstHost, src, dst)) < 0) 1552 hc_chflags(&DstHost, dst, flags); 1553 #endif 1554 } 1555 return(r); 1556 } 1557 1558 static int 1559 xlink(const char *src, const char *dst, u_long flags) 1560 { 1561 int r; 1562 #ifdef _ST_FLAGS_PRESENT_ 1563 int e; 1564 #endif 1565 1566 r = 0; 1567 1568 if ((r = hc_link(&DstHost, src, dst)) < 0) { 1569 #ifdef _ST_FLAGS_PRESENT_ 1570 hc_chflags(&DstHost, src, 0); 1571 r = hc_link(&DstHost, src, dst); 1572 e = errno; 1573 hc_chflags(&DstHost, src, flags); 1574 errno = e; 1575 #endif 1576 } 1577 if (r == 0) 1578 ++CountLinkedItems; 1579 return(r); 1580 } 1581 1582