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