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