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