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