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