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