xref: /dflybsd-src/bin/cpdup/cpdup.c (revision 0ea552d68c3a0ee380451241d06e39c6dd43d66a)
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.16 2006/08/18 16:05: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	16384
63 #define HMASK	(HSIZE-1)
64 #define HLSIZE	8192
65 #define HLMASK	(HLSIZE - 1)
66 
67 #ifndef _ST_FLAGS_PRESENT_
68 #define st_flags	st_mode
69 #endif
70 
71 typedef struct Node {
72     struct Node *no_Next;
73     struct Node *no_HNext;
74     int  no_Value;
75     char no_Name[4];
76 } Node;
77 
78 typedef struct List {
79     Node	li_Node;
80     Node	*li_Hash[HSIZE];
81 } List;
82 
83 struct hlink {
84     ino_t ino;
85     ino_t dino;
86     struct hlink *next;
87     struct hlink *prev;
88     nlink_t nlinked;
89     char name[0];
90 };
91 
92 struct hlink *hltable[HLSIZE];
93 
94 void RemoveRecur(const char *dpath, dev_t devNo);
95 void InitList(List *list);
96 void ResetList(List *list);
97 int AddList(List *list, const char *name, int n);
98 static struct hlink *hltlookup(struct stat *);
99 static struct hlink *hltadd(struct stat *, const char *);
100 static char *checkHLPath(struct stat *st, const char *spath, const char *dpath);
101 static int shash(const char *s);
102 static void hltdelete(struct hlink *);
103 int YesNo(const char *path);
104 static int xrename(const char *src, const char *dst, u_long flags);
105 static int xlink(const char *src, const char *dst, u_long flags);
106 int WildCmp(const char *s1, const char *s2);
107 int DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo);
108 
109 int AskConfirmation = 1;
110 int SafetyOpt = 1;
111 int ForceOpt;
112 int VerboseOpt;
113 int QuietOpt;
114 int NoRemoveOpt;
115 int UseMD5Opt;
116 int UseFSMIDOpt;
117 int SummaryOpt;
118 int SlaveOpt;
119 int EnableDirectoryRetries;
120 int DstBaseLen;
121 char IOBuf1[65536];
122 char IOBuf2[65536];
123 const char *UseCpFile;
124 const char *UseHLPath;
125 const char *MD5CacheFile;
126 const char *FSMIDCacheFile;
127 
128 int64_t CountSourceBytes;
129 int64_t CountSourceItems;
130 int64_t CountCopiedItems;
131 int64_t CountReadBytes;
132 int64_t CountWriteBytes;
133 int64_t CountRemovedItems;
134 
135 struct HostConf SrcHost;
136 struct HostConf DstHost;
137 
138 int
139 main(int ac, char **av)
140 {
141     int i;
142     char *src = NULL;
143     char *dst = NULL;
144     char *ptr;
145     struct timeval start;
146 
147     signal(SIGPIPE, SIG_IGN);
148 
149     gettimeofday(&start, NULL);
150     for (i = 1; i < ac; ++i) {
151 	int v = 1;
152 
153 	ptr = av[i];
154 	if (*ptr != '-') {
155 	    if (src == NULL) {
156 		src = ptr;
157 	    } else if (dst == NULL) {
158 		dst = ptr;
159 	    } else {
160 		fatal("too many arguments");
161 		/* not reached */
162 	    }
163 	    continue;
164 	}
165 	ptr += 2;
166 
167 	if (*ptr)
168 	    v = strtol(ptr, NULL, 0);
169 
170 	switch(ptr[-1]) {
171 	case 'v':
172 	    VerboseOpt = 1;
173 	    while (*ptr == 'v') {
174 		++VerboseOpt;
175 		++ptr;
176 	    }
177 	    if (*ptr >= '0' && *ptr <= '9')
178 		VerboseOpt = strtol(ptr, NULL, 0);
179 	    break;
180 	case 'I':
181 	    SummaryOpt = v;
182 	    break;
183 	case 'o':
184 	    NoRemoveOpt = v;
185 	    break;
186 	case 'x':
187 	    UseCpFile = ".cpignore";
188 	    break;
189 	case 'X':
190 	    UseCpFile = (*ptr) ? ptr : av[++i];
191 	    break;
192 	case 'H':
193 	    UseHLPath = (*ptr) ? ptr : av[++i];
194 	    break;
195 	case 'S':
196 	    SlaveOpt = v;
197 	    break;
198 	case 'f':
199 	    ForceOpt = v;
200 	    break;
201 	case 'i':
202 	    AskConfirmation = v;
203 	    break;
204 	case 's':
205 	    SafetyOpt = v;
206 	    break;
207 	case 'q':
208 	    QuietOpt = v;
209 	    break;
210 	case 'k':
211 	    UseFSMIDOpt = v;
212 	    FSMIDCacheFile = ".FSMID.CHECK";
213 	    break;
214 	case 'K':
215 	    UseFSMIDOpt = v;
216 	    FSMIDCacheFile = av[++i];
217 	    break;
218 	case 'M':
219 	    UseMD5Opt = v;
220 	    MD5CacheFile = av[++i];
221 	    break;
222 	case 'm':
223 	    UseMD5Opt = v;
224 	    MD5CacheFile = ".MD5.CHECKSUMS";
225 	    break;
226 	case 'u':
227 	    setvbuf(stdout, NULL, _IOLBF, 0);
228 	    break;
229 	default:
230 	    fatal("illegal option: %s\n", ptr - 2);
231 	    /* not reached */
232 	    break;
233 	}
234     }
235 
236     /*
237      * If we are told to go into slave mode, run the HC protocol
238      */
239     if (SlaveOpt) {
240 	hc_slave(0, 1);
241 	exit(0);
242     }
243 
244     /*
245      * Extract the source and/or/neither target [user@]host and
246      * make any required connections.
247      */
248     if (src && (ptr = strchr(src, ':')) != NULL) {
249 	asprintf(&SrcHost.host, "%*.*s", ptr - src, ptr - src, src);
250 	src = ptr + 1;
251 	if (UseCpFile) {
252 	    fprintf(stderr, "The cpignore options are not currently supported for remote sources\n");
253 	    exit(1);
254 	}
255 	if (UseMD5Opt) {
256 	    fprintf(stderr, "The MD5 options are not currently supported for remote sources\n");
257 	    exit(1);
258 	}
259 	if (hc_connect(&SrcHost) < 0)
260 	    fprintf(stderr, "Unable to connect to %s\n", SrcHost.host);
261     }
262     if (dst && (ptr = strchr(dst, ':')) != NULL) {
263 	asprintf(&DstHost.host, "%*.*s", ptr - dst, ptr - dst, dst);
264 	dst = ptr + 1;
265 	if (UseFSMIDOpt) {
266 	    fprintf(stderr, "The FSMID options are not currently supported for remote targets\n");
267 	    exit(1);
268 	}
269 	if (hc_connect(&DstHost) < 0)
270 	    fprintf(stderr, "Unable to connect to %s\n", DstHost.host);
271     }
272 
273     /*
274      * dst may be NULL only if -m option is specified,
275      * which forces an update of the MD5 checksums
276      */
277     if (dst == NULL && UseMD5Opt == 0) {
278 	fatal(NULL);
279 	/* not reached */
280     }
281     if (dst) {
282 	DstBaseLen = strlen(dst);
283 	i = DoCopy(src, dst, (dev_t)-1, (dev_t)-1);
284     } else {
285 	i = DoCopy(src, NULL, (dev_t)-1, (dev_t)-1);
286     }
287 #ifndef NOMD5
288     md5_flush();
289 #endif
290     fsmid_flush();
291 
292     if (SummaryOpt && i == 0) {
293 	long duration;
294 	struct timeval end;
295 
296 	gettimeofday(&end, NULL);
297 	CountSourceBytes += sizeof(struct stat) * CountSourceItems;
298 	CountReadBytes += sizeof(struct stat) * CountSourceItems;
299 	CountWriteBytes +=  sizeof(struct stat) * CountCopiedItems;
300 	CountWriteBytes +=  sizeof(struct stat) * CountRemovedItems;
301 
302 	duration = end.tv_sec - start.tv_sec;
303 	duration *= 1000000;
304 	duration += end.tv_usec - start.tv_usec;
305 	if (duration == 0) duration = 1;
306 	logstd("cpdup completed successfully\n");
307 	logstd("%lld bytes source %lld bytes read %lld bytes written (%.1fX speedup)\n",
308 	    (long long)CountSourceBytes,
309 	    (long long)CountReadBytes,
310 	    (long long)CountWriteBytes,
311 	    ((double)CountSourceBytes * 2.0) / ((double)(CountReadBytes + CountWriteBytes)));
312  	logstd("%lld source items %lld items copied %lld things deleted\n",
313 	    (long long)CountSourceItems,
314 	    (long long)CountCopiedItems,
315 	    (long long)CountRemovedItems);
316 	logstd("%.1f seconds %5d Kbytes/sec synced %5d Kbytes/sec scanned\n",
317 	    (float)duration / (float)1000000,
318 	    (long)((long)1000000 * (CountReadBytes + CountWriteBytes) / duration  / 1024.0),
319 	    (long)((long)1000000 * CountSourceBytes / duration / 1024.0));
320     }
321     exit((i == 0) ? 0 : 1);
322 }
323 
324 static struct hlink *
325 hltlookup(struct stat *stp)
326 {
327     struct hlink *hl;
328     int n;
329 
330     n = stp->st_ino & HLMASK;
331 
332     for (hl = hltable[n]; hl; hl = hl->next)
333         if (hl->ino == stp->st_ino)
334               return hl;
335 
336     return NULL;
337 }
338 
339 static struct hlink *
340 hltadd(struct stat *stp, const char *path)
341 {
342     struct hlink *new;
343     int plen = strlen(path);
344     int n;
345 
346     new = malloc(offsetof(struct hlink, name[plen + 1]));
347     if (new == NULL) {
348         fprintf(stderr, "out of memory\n");
349         exit(EXIT_FAILURE);
350     }
351 
352     /* initialize and link the new element into the table */
353     new->ino = stp->st_ino;
354     new->dino = 0;
355     bcopy(path, new->name, plen + 1);
356     new->nlinked = 1;
357     new->prev = NULL;
358     n = stp->st_ino & HLMASK;
359     new->next = hltable[n];
360     if (hltable[n])
361         hltable[n]->prev = new;
362     hltable[n] = new;
363 
364     return new;
365 }
366 
367 static void
368 hltdelete(struct hlink *hl)
369 {
370     if (hl->prev) {
371         if (hl->next)
372             hl->next->prev = hl->prev;
373         hl->prev->next = hl->next;
374     } else {
375         if (hl->next)
376             hl->next->prev = NULL;
377 
378         hltable[hl->ino & HLMASK] = hl->next;
379     }
380 
381     free(hl);
382 }
383 
384 /*
385  * If UseHLPath is defined check to see if the file in question is
386  * the same as the source file, and if it is return a pointer to the
387  * -H path based file for hardlinking.  Else return NULL.
388  */
389 static char *
390 checkHLPath(struct stat *st1, const char *spath, const char *dpath)
391 {
392     struct stat sthl;
393     char *hpath;
394     int fd1;
395     int fd2;
396     int good;
397 
398     asprintf(&hpath, "%s%s", UseHLPath, dpath + DstBaseLen);
399 
400     /*
401      * stat info matches ?
402      */
403     if (hc_stat(&DstHost, hpath, &sthl) < 0 ||
404 	st1->st_size != sthl.st_size ||
405 	st1->st_uid != sthl.st_uid ||
406 	st1->st_gid != sthl.st_gid ||
407 	st1->st_mtime != sthl.st_mtime
408     ) {
409 	free(hpath);
410 	return(NULL);
411     }
412 
413     /*
414      * If ForceOpt is set we have to compare the files
415      */
416     if (ForceOpt) {
417 	fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0);
418 	fd2 = hc_open(&DstHost, hpath, O_RDONLY, 0);
419 	good = 0;
420 
421 	if (fd1 >= 0 && fd2 >= 0) {
422 	    int n;
423 
424 	    while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
425 		if (hc_read(&DstHost, fd2, IOBuf2, sizeof(IOBuf2)) != n)
426 		    break;
427 		if (bcmp(IOBuf1, IOBuf2, n) != 0)
428 		    break;
429 	    }
430 	    if (n == 0)
431 		good = 1;
432 	}
433 	if (fd1 >= 0)
434 	    hc_close(&SrcHost, fd1);
435 	if (fd2 >= 0)
436 	    hc_close(&DstHost, fd2);
437 	if (good == 0) {
438 	    free(hpath);
439 	    hpath = NULL;
440 	}
441     }
442     return(hpath);
443 }
444 
445 int
446 DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
447 {
448     struct stat st1;
449     struct stat st2;
450     int r, mres, fres, st2Valid;
451     struct hlink *hln;
452     List list;
453     u_int64_t size;
454 
455     InitList(&list);
456     r = mres = fres = st2Valid = 0;
457     size = 0;
458     hln = NULL;
459 
460     if (hc_lstat(&SrcHost, spath, &st1) != 0)
461 	return(0);
462     st2.st_mode = 0;	/* in case lstat fails */
463     st2.st_flags = 0;	/* in case lstat fails */
464     if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0)
465 	st2Valid = 1;
466 
467     if (S_ISREG(st1.st_mode)) {
468 	size = st1.st_blocks * 512;
469 	if (st1.st_size % 512)
470 	    size += st1.st_size % 512 - 512;
471     }
472 
473     /*
474      * Handle hardlinks
475      */
476 
477     if (S_ISREG(st1.st_mode) && st1.st_nlink > 1 && dpath) {
478         if ((hln = hltlookup(&st1)) != NULL) {
479             hln->nlinked++;
480 
481             if (st2Valid) {
482                 if (st2.st_ino == hln->dino) {
483 		    /*
484 		     * hard link is already correct, nothing to do
485 		     */
486 		    if (VerboseOpt >= 3)
487 			logstd("%-32s nochange\n", (dpath) ? dpath : spath);
488                     if (hln->nlinked == st1.st_nlink)
489                         hltdelete(hln);
490 		    CountSourceItems++;
491                     return 0;
492                 } else {
493 		    /*
494 		     * hard link is not correct, attempt to unlink it
495 		     */
496                     if (hc_remove(&DstHost, dpath) < 0) {
497 			logerr("%-32s hardlink: unable to unlink: %s\n",
498 			    ((dpath) ? dpath : spath), strerror(errno));
499                         hltdelete(hln);
500 			return (r + 1);
501 		    }
502                 }
503             }
504 
505             if (xlink(hln->name, dpath, st1.st_flags) < 0) {
506 		int tryrelink = (errno == EMLINK);
507 		logerr("%-32s hardlink: unable to link to %s: %s\n",
508 		    (dpath ? dpath : spath), hln->name, strerror(errno)
509 		);
510                 hltdelete(hln);
511                 hln = NULL;
512 		if (tryrelink) {
513 		    logerr("%-20s hardlink: will attempt to copy normally\n");
514 		    goto relink;
515 		}
516 		++r;
517             } else {
518                 if (hln->nlinked == st1.st_nlink) {
519                     hltdelete(hln);
520 		    hln = NULL;
521 		}
522                 if (r == 0) {
523 		    if (VerboseOpt) {
524 			logstd("%-32s hardlink: %s\n",
525 			    (dpath ? dpath : spath),
526 			    (st2Valid ? "relinked" : "linked")
527 			);
528 		    }
529 		    CountSourceItems++;
530 		    CountCopiedItems++;
531                     return 0;
532 		}
533             }
534         } else {
535 	    /*
536 	     * first instance of hardlink must be copied normally
537 	     */
538 relink:
539             hln = hltadd(&st1, dpath);
540 	}
541     }
542 
543     /*
544      * Do we need to copy the file/dir/link/whatever?  Early termination
545      * if we do not.  Always redo links.  Directories are always traversed
546      * except when the FSMID options are used.
547      *
548      * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good.
549      */
550 
551     if (
552 	st2Valid
553 	&& st1.st_mode == st2.st_mode
554 #ifdef _ST_FLAGS_PRESENT_
555 	&& st1.st_flags == st2.st_flags
556 #endif
557     ) {
558 	if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) {
559 	    /*
560 	     * If FSMID tracking is turned on we can avoid recursing through
561 	     * an entire directory subtree if the FSMID matches.
562 	     */
563 #ifdef _ST_FSMID_PRESENT_
564 	    if (ForceOpt == 0 &&
565 		(UseFSMIDOpt && (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
566 	    ) {
567 		if (VerboseOpt >= 3) {
568 		    if (UseFSMIDOpt)
569 			logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
570 		    else
571 			logstd("%-32s nochange\n", (dpath ? dpath : spath));
572 		}
573 		return(0);
574 	    }
575 #endif
576 	} else {
577 	    if (ForceOpt == 0 &&
578 		st1.st_size == st2.st_size &&
579 		st1.st_uid == st2.st_uid &&
580 		st1.st_gid == st2.st_gid &&
581 		st1.st_mtime == st2.st_mtime
582 #ifndef NOMD5
583 		&& (UseMD5Opt == 0 || (mres = md5_check(spath, dpath)) == 0)
584 #endif
585 #ifdef _ST_FSMID_PRESENT_
586 		&& (UseFSMIDOpt == 0 || (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
587 #endif
588 	    ) {
589                 if (hln)
590                     hln->dino = st2.st_ino;
591 		if (VerboseOpt >= 3) {
592 #ifndef NOMD5
593 		    if (UseMD5Opt)
594 			logstd("%-32s md5-nochange\n", (dpath ? dpath : spath));
595 		    else
596 #endif
597 		    if (UseFSMIDOpt)
598 			logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
599 		    else
600 			logstd("%-32s nochange\n", (dpath ? dpath : spath));
601 		}
602 		CountSourceBytes += size;
603 		CountSourceItems++;
604 
605 		return(0);
606 	    }
607 	}
608     }
609     if (st2Valid && !S_ISDIR(st1.st_mode) && S_ISDIR(st2.st_mode)) {
610 	if (SafetyOpt) {
611 	    logerr("%-32s SAFETY - refusing to copy file over directory\n",
612 		(dpath ? dpath : spath)
613 	    );
614 	    ++r;		/* XXX */
615 	    return(0);	/* continue with the cpdup anyway */
616 	}
617 	if (QuietOpt == 0 || AskConfirmation) {
618 	    logstd("%-32s WARNING: non-directory source will blow away\n"
619 		   "%-32s preexisting dest directory, continuing anyway!\n",
620 		   ((dpath) ? dpath : spath), "");
621 	}
622 	if (dpath)
623 	    RemoveRecur(dpath, ddevNo);
624     }
625 
626     /*
627      * The various comparisons failed, copy it.
628      */
629     if (S_ISDIR(st1.st_mode)) {
630 	DIR *dir;
631 
632 	if (fres < 0)
633 	    logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath);
634 	if ((dir = hc_opendir(&SrcHost, spath)) != NULL) {
635 	    struct dirent *den;
636 	    int noLoop = 0;
637 
638 	    if (dpath) {
639 		if (S_ISDIR(st2.st_mode) == 0) {
640 		    hc_remove(&DstHost, dpath);
641 		    if (hc_mkdir(&DstHost, dpath, st1.st_mode | 0700) != 0) {
642 			logerr("%s: mkdir failed: %s\n",
643 			    (dpath ? dpath : spath), strerror(errno));
644 			r = 1;
645 			noLoop = 1;
646 		    }
647 		    /*
648 		     * Matt: why don't you check error codes here?
649 		     */
650 		    hc_lstat(&DstHost, dpath, &st2);
651 		    hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid);
652 		    CountCopiedItems++;
653 		} else {
654 		    /*
655 		     * Directory must be scanable by root for cpdup to
656 		     * work.  We'll fix it later if the directory isn't
657 		     * supposed to be readable ( which is why we fixup
658 		     * st2.st_mode to match what we did ).
659 		     */
660 		    if ((st2.st_mode & 0700) != 0700) {
661 			hc_chmod(&DstHost, dpath, st2.st_mode | 0700);
662 			st2.st_mode |= 0700;
663 		    }
664 		    if (VerboseOpt >= 2)
665 			logstd("%s\n", dpath ? dpath : spath);
666 		}
667 	    }
668 
669 	    if ((int)sdevNo >= 0 && st1.st_dev != sdevNo) {
670 		noLoop = 1;
671 	    } else {
672 		sdevNo = st1.st_dev;
673 	    }
674 
675 	    if ((int)ddevNo >= 0 && st2.st_dev != ddevNo) {
676 		noLoop = 1;
677 	    } else {
678 		ddevNo = st2.st_dev;
679 	    }
680 
681 	    /*
682 	     * scan .cpignore file for files/directories
683 	     * to ignore.
684 	     */
685 
686 	    if (UseCpFile) {
687 		FILE *fi;
688 		char buf[8192];
689 		char *fpath;
690 
691 		if (UseCpFile[0] == '/') {
692 		    fpath = mprintf("%s", UseCpFile);
693 		} else {
694 		    fpath = mprintf("%s/%s", spath, UseCpFile);
695 		}
696 		AddList(&list, strrchr(fpath, '/') + 1, 1);
697 		if ((fi = fopen(fpath, "r")) != NULL) {
698 		    while (fgets(buf, sizeof(buf), fi) != NULL) {
699 			int l = strlen(buf);
700 			CountReadBytes += l;
701 			if (l && buf[l-1] == '\n')
702 			    buf[--l] = 0;
703 			if (buf[0] && buf[0] != '#')
704 			    AddList(&list, buf, 1);
705 		    }
706 		    fclose(fi);
707 		}
708 		free(fpath);
709 	    }
710 
711 	    /*
712 	     * Automatically exclude MD5CacheFile that we create on the
713 	     * source from the copy to the destination.
714 	     *
715 	     * Automatically exclude a FSMIDCacheFile on the source that
716 	     * would otherwise overwrite the one we maintain on the target.
717 	     */
718 	    if (UseMD5Opt)
719 		AddList(&list, MD5CacheFile, 1);
720 	    if (UseFSMIDOpt)
721 		AddList(&list, FSMIDCacheFile, 1);
722 
723 	    while (noLoop == 0 && (den = hc_readdir(&SrcHost, dir)) != NULL) {
724 		/*
725 		 * ignore . and ..
726 		 */
727 		char *nspath;
728 		char *ndpath = NULL;
729 
730 		if (strcmp(den->d_name, ".") == 0 ||
731 		    strcmp(den->d_name, "..") == 0
732 		) {
733 		    continue;
734 		}
735 		/*
736 		 * ignore if on .cpignore list
737 		 */
738 		if (AddList(&list, den->d_name, 0) == 1) {
739 		    continue;
740 		}
741 		nspath = mprintf("%s/%s", spath, den->d_name);
742 		if (dpath)
743 		    ndpath = mprintf("%s/%s", dpath, den->d_name);
744 		r += DoCopy(
745 		    nspath,
746 		    ndpath,
747 		    sdevNo,
748 		    ddevNo
749 		);
750 		free(nspath);
751 		if (ndpath)
752 		    free(ndpath);
753 	    }
754 
755 	    hc_closedir(&SrcHost, dir);
756 
757 	    /*
758 	     * Remove files/directories from destination that do not appear
759 	     * in the source.
760 	     */
761 	    if (dpath && (dir = hc_opendir(&DstHost, dpath)) != NULL) {
762 		while (noLoop == 0 && (den = hc_readdir(&DstHost, dir)) != NULL) {
763 		    /*
764 		     * ignore . or ..
765 		     */
766 		    if (strcmp(den->d_name, ".") == 0 ||
767 			strcmp(den->d_name, "..") == 0
768 		    ) {
769 			continue;
770 		    }
771 		    /*
772 		     * If object does not exist in source or .cpignore
773 		     * then recursively remove it.
774 		     */
775 		    if (AddList(&list, den->d_name, 3) == 3) {
776 			char *ndpath;
777 
778 			ndpath = mprintf("%s/%s", dpath, den->d_name);
779 			RemoveRecur(ndpath, ddevNo);
780 			free(ndpath);
781 		    }
782 		}
783 		hc_closedir(&DstHost, dir);
784 	    }
785 
786 	    if (dpath) {
787 		struct timeval tv[2];
788 
789 		if (ForceOpt ||
790 		    st2Valid == 0 ||
791 		    st1.st_uid != st2.st_uid ||
792 		    st1.st_gid != st2.st_gid
793 		) {
794 		    hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid);
795 		}
796 		if (st2Valid == 0 || st1.st_mode != st2.st_mode) {
797 		    hc_chmod(&DstHost, dpath, st1.st_mode);
798 		}
799 #ifdef _ST_FLAGS_PRESENT_
800 		if (st2Valid == 0 || st1.st_flags != st2.st_flags) {
801 		    hc_chflags(&DstHost, dpath, st1.st_flags);
802 		}
803 #endif
804 		if (ForceOpt ||
805 		    st2Valid == 0 ||
806 		    st1.st_mtime != st2.st_mtime
807 		) {
808 		    bzero(tv, sizeof(tv));
809 		    tv[0].tv_sec = st1.st_mtime;
810 		    tv[1].tv_sec = st1.st_mtime;
811 		    hc_utimes(&DstHost, dpath, tv);
812 		}
813 	    }
814 	}
815     } else if (dpath == NULL) {
816 	/*
817 	 * If dpath is NULL, we are just updating the MD5
818 	 */
819 #ifndef NOMD5
820 	if (UseMD5Opt && S_ISREG(st1.st_mode)) {
821 	    mres = md5_check(spath, NULL);
822 
823 	    if (VerboseOpt > 1) {
824 		if (mres < 0)
825 		    logstd("%-32s md5-update\n", (dpath) ? dpath : spath);
826 		else
827 		    logstd("%-32s md5-ok\n", (dpath) ? dpath : spath);
828 	    } else if (!QuietOpt && mres < 0) {
829 		logstd("%-32s md5-update\n", (dpath) ? dpath : spath);
830 	    }
831 	}
832 #endif
833     } else if (S_ISREG(st1.st_mode)) {
834 	char *path;
835 	char *hpath;
836 	int fd1;
837 	int fd2;
838 
839 	path = mprintf("%s.tmp", dpath);
840 
841 	/*
842 	 * Handle check failure message.
843 	 */
844 #ifndef NOMD5
845 	if (mres < 0)
846 	    logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath);
847 	else
848 #endif
849 	if (fres < 0)
850 	    logerr("%-32s fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath);
851 
852 	/*
853 	 * Not quite ready to do the copy yet.  If UseHLPath is defined,
854 	 * see if we can hardlink instead.
855 	 */
856 
857 	if (UseHLPath && (hpath = checkHLPath(&st1, spath, dpath)) != NULL) {
858 		if (hc_link(&DstHost, hpath, dpath) == 0) {
859 			if (VerboseOpt) {
860 			    logstd("%-32s hardlinked(-H)\n",
861 				   (dpath ? dpath : spath));
862 			}
863 			free(hpath);
864 			goto skip_copy;
865 		}
866 		/*
867 		 * Shucks, we may have hit a filesystem hard linking limit,
868 		 * we have to copy instead.
869 		 */
870 		free(hpath);
871 	}
872 
873 	if ((fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0)) >= 0) {
874 	    if ((fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
875 		/*
876 		 * There could be a .tmp file from a previously interrupted
877 		 * run, delete and retry.  Fail if we still can't get at it.
878 		 */
879 #ifdef _ST_FLAGS_PRESENT_
880 		hc_chflags(&DstHost, path, 0);
881 #endif
882 		hc_remove(&DstHost, path);
883 		fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
884 	    }
885 	    if (fd2 >= 0) {
886 		const char *op;
887 		int n;
888 
889 		/*
890 		 * Matt: What about holes?
891 		 */
892 		op = "read";
893 		while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
894 		    op = "write";
895 		    if (hc_write(&DstHost, fd2, IOBuf1, n) != n)
896 			break;
897 		    op = "read";
898 		}
899 		hc_close(&DstHost, fd2);
900 		if (n == 0) {
901 		    struct timeval tv[2];
902 
903 		    bzero(tv, sizeof(tv));
904 		    tv[0].tv_sec = st1.st_mtime;
905 		    tv[1].tv_sec = st1.st_mtime;
906 
907 		    hc_utimes(&DstHost, path, tv);
908 		    hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
909 		    hc_chmod(&DstHost, path, st1.st_mode);
910 		    if (xrename(path, dpath, st2.st_flags) != 0) {
911 			logerr("%-32s rename-after-copy failed: %s\n",
912 			    (dpath ? dpath : spath), strerror(errno)
913 			);
914 			++r;
915 		    } else {
916 			if (VerboseOpt)
917 			    logstd("%-32s copy-ok\n", (dpath ? dpath : spath));
918 #ifdef _ST_FLAGS_PRESENT_
919 			if (st1.st_flags)
920 			    hc_chflags(&DstHost, dpath, st1.st_flags);
921 #endif
922 		    }
923 		    CountReadBytes += size;
924 		    CountWriteBytes += size;
925 		    CountSourceBytes += size;
926 		    CountSourceItems++;
927 		    CountCopiedItems++;
928 		} else {
929 		    logerr("%-32s %s failed: %s\n",
930 			(dpath ? dpath : spath), op, strerror(errno)
931 		    );
932 		    hc_remove(&DstHost, path);
933 		    ++r;
934 		}
935 	    } else {
936 		logerr("%-32s create (uid %d, euid %d) failed: %s\n",
937 		    (dpath ? dpath : spath), getuid(), geteuid(),
938 		    strerror(errno)
939 		);
940 		++r;
941 	    }
942 	    hc_close(&SrcHost, fd1);
943 	} else {
944 	    logerr("%-32s copy: open failed: %s\n",
945 		(dpath ? dpath : spath),
946 		strerror(errno)
947 	    );
948 	    ++r;
949 	}
950 skip_copy:
951 	free(path);
952 
953         if (hln) {
954             if (!r && hc_stat(&DstHost, dpath, &st2) == 0)
955                 hln->dino = st2.st_ino;
956             else
957                 hltdelete(hln);
958         }
959     } else if (S_ISLNK(st1.st_mode)) {
960 	char link1[1024];
961 	char link2[1024];
962 	char path[2048];
963 	int n1;
964 	int n2;
965 
966 	snprintf(path, sizeof(path), "%s.tmp", dpath);
967 	n1 = hc_readlink(&SrcHost, spath, link1, sizeof(link1) - 1);
968 	n2 = hc_readlink(&DstHost, dpath, link2, sizeof(link2) - 1);
969 	if (n1 >= 0) {
970 	    if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) {
971 		hc_umask(&DstHost, ~st1.st_mode);
972 		hc_remove(&DstHost, path);
973 		link1[n1] = 0;
974 		if (hc_symlink(&DstHost, link1, path) < 0) {
975                       logerr("%-32s symlink (%s->%s) failed: %s\n",
976 			  (dpath ? dpath : spath), link1, path,
977 			  strerror(errno)
978 		      );
979 		      ++r;
980 		} else {
981 		    hc_lchown(&DstHost, path, st1.st_uid, st1.st_gid);
982 		    /*
983 		     * there is no lchmod() or lchflags(), we
984 		     * cannot chmod or chflags a softlink.
985 		     */
986 		    if (xrename(path, dpath, st2.st_flags) != 0) {
987 			logerr("%-32s rename softlink (%s->%s) failed: %s\n",
988 			    (dpath ? dpath : spath),
989 			    path, dpath, strerror(errno));
990 		    } else if (VerboseOpt) {
991 			logstd("%-32s softlink-ok\n", (dpath ? dpath : spath));
992 		    }
993 		    hc_umask(&DstHost, 000);
994 		    CountWriteBytes += n1;
995 		    CountCopiedItems++;
996 	  	}
997 	    } else {
998 		if (VerboseOpt >= 3)
999 		    logstd("%-32s nochange\n", (dpath ? dpath : spath));
1000 	    }
1001 	    CountSourceBytes += n1;
1002 	    CountReadBytes += n1;
1003 	    if (n2 > 0) CountReadBytes += n2;
1004 	    CountSourceItems++;
1005 	} else {
1006 	    r = 1;
1007 	    logerr("%-32s softlink-failed\n", (dpath ? dpath : spath));
1008 	}
1009     } else if (S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) {
1010 	char path[2048];
1011 
1012 	if (ForceOpt ||
1013 	    st2Valid == 0 ||
1014 	    st1.st_mode != st2.st_mode ||
1015 	    st1.st_rdev != st2.st_rdev ||
1016 	    st1.st_uid != st2.st_uid ||
1017 	    st1.st_gid != st2.st_gid
1018 	) {
1019 	    snprintf(path, sizeof(path), "%s.tmp", dpath);
1020 
1021 	    hc_remove(&DstHost, path);
1022 	    if (mknod(path, st1.st_mode, st1.st_rdev) == 0) {
1023 		hc_chmod(&DstHost, path, st1.st_mode);
1024 		hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
1025 		hc_remove(&DstHost, dpath);
1026 		if (xrename(path, dpath, st2.st_flags) != 0) {
1027 		    logerr("%-32s dev-rename-after-create failed: %s\n",
1028 			(dpath ? dpath : spath),
1029 			strerror(errno)
1030 		    );
1031 		} else if (VerboseOpt) {
1032 		    logstd("%-32s dev-ok\n", (dpath ? dpath : spath));
1033 		}
1034 		CountCopiedItems++;
1035 	    } else {
1036 		r = 1;
1037 		logerr("%-32s dev failed: %s\n",
1038 		    (dpath ? dpath : spath), strerror(errno)
1039 		);
1040 	    }
1041 	} else {
1042 	    if (VerboseOpt >= 3)
1043 		logstd("%-32s nochange\n", (dpath ? dpath : spath));
1044 	}
1045 	CountSourceItems++;
1046     }
1047     ResetList(&list);
1048     return (r);
1049 }
1050 
1051 /*
1052  * RemoveRecur()
1053  */
1054 
1055 void
1056 RemoveRecur(const char *dpath, dev_t devNo)
1057 {
1058     struct stat st;
1059 
1060     if (hc_lstat(&DstHost, dpath, &st) == 0) {
1061 	if ((int)devNo < 0)
1062 	    devNo = st.st_dev;
1063 	if (st.st_dev == devNo) {
1064 	    if (S_ISDIR(st.st_mode)) {
1065 		DIR *dir;
1066 
1067 		if ((dir = hc_opendir(&DstHost, dpath)) != NULL) {
1068 		    struct dirent *den;
1069 		    while ((den = hc_readdir(&DstHost, dir)) != NULL) {
1070 			char *ndpath;
1071 
1072 			if (strcmp(den->d_name, ".") == 0)
1073 			    continue;
1074 			if (strcmp(den->d_name, "..") == 0)
1075 			    continue;
1076 			ndpath = mprintf("%s/%s", dpath, den->d_name);
1077 			RemoveRecur(ndpath, devNo);
1078 			free(ndpath);
1079 		    }
1080 		    hc_closedir(&DstHost, dir);
1081 		}
1082 		if (AskConfirmation && NoRemoveOpt == 0) {
1083 		    if (YesNo(dpath)) {
1084 			if (hc_rmdir(&DstHost, dpath) < 0) {
1085 			    logerr("%-32s rmdir failed: %s\n",
1086 				dpath, strerror(errno)
1087 			    );
1088 			}
1089 			CountRemovedItems++;
1090 		    }
1091 		} else {
1092 		    if (NoRemoveOpt) {
1093 			if (VerboseOpt)
1094 			    logstd("%-32s not-removed\n", dpath);
1095 		    } else if (hc_rmdir(&DstHost, dpath) == 0) {
1096 			if (VerboseOpt)
1097 			    logstd("%-32s rmdir-ok\n", dpath);
1098 			CountRemovedItems++;
1099 		    } else {
1100 			logerr("%-32s rmdir failed: %s\n",
1101 			    dpath, strerror(errno)
1102 			);
1103 		    }
1104 		}
1105 	    } else {
1106 		if (AskConfirmation && NoRemoveOpt == 0) {
1107 		    if (YesNo(dpath)) {
1108 			if (hc_remove(&DstHost, dpath) < 0) {
1109 			    logerr("%-32s remove failed: %s\n",
1110 				dpath, strerror(errno)
1111 			    );
1112 			}
1113 			CountRemovedItems++;
1114 		    }
1115 		} else {
1116 		    if (NoRemoveOpt) {
1117 			if (VerboseOpt)
1118 			    logstd("%-32s not-removed\n", dpath);
1119 		    } else if (hc_remove(&DstHost, dpath) == 0) {
1120 			if (VerboseOpt)
1121 			    logstd("%-32s remove-ok\n", dpath);
1122 			CountRemovedItems++;
1123 		    } else {
1124 			logerr("%-32s remove failed: %s\n",
1125 			    dpath, strerror(errno)
1126 			);
1127 		    }
1128 		}
1129 	    }
1130 	}
1131     }
1132 }
1133 
1134 void
1135 InitList(List *list)
1136 {
1137     bzero(list, sizeof(List));
1138     list->li_Node.no_Next = &list->li_Node;
1139 }
1140 
1141 void
1142 ResetList(List *list)
1143 {
1144     Node *node;
1145 
1146     while ((node = list->li_Node.no_Next) != &list->li_Node) {
1147 	list->li_Node.no_Next = node->no_Next;
1148 	free(node);
1149     }
1150     InitList(list);
1151 }
1152 
1153 int
1154 AddList(List *list, const char *name, int n)
1155 {
1156     Node *node;
1157     int hv;
1158 
1159     hv = shash(name);
1160 
1161     /*
1162      * Scan against wildcards.  Only a node value of 1 can be a wildcard
1163      * ( usually scanned from .cpignore )
1164      */
1165 
1166     for (node = list->li_Hash[0]; node; node = node->no_HNext) {
1167 	if (strcmp(name, node->no_Name) == 0 ||
1168 	    (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0)
1169 	) {
1170 	    return(node->no_Value);
1171 	}
1172     }
1173 
1174     /*
1175      * Look for exact match
1176      */
1177 
1178     for (node = list->li_Hash[hv]; node; node = node->no_HNext) {
1179 	if (strcmp(name, node->no_Name) == 0) {
1180 	    return(node->no_Value);
1181 	}
1182     }
1183     node = malloc(sizeof(Node) + strlen(name) + 1);
1184     if (node == NULL) {
1185         fprintf(stderr, "out of memory\n");
1186         exit(EXIT_FAILURE);
1187     }
1188 
1189     node->no_Next = list->li_Node.no_Next;
1190     list->li_Node.no_Next = node;
1191 
1192     node->no_HNext = list->li_Hash[hv];
1193     list->li_Hash[hv] = node;
1194 
1195     strcpy(node->no_Name, name);
1196     node->no_Value = n;
1197 
1198     return(n);
1199 }
1200 
1201 static int
1202 shash(const char *s)
1203 {
1204     int hv;
1205 
1206     hv = 0xA4FB3255;
1207 
1208     while (*s) {
1209 	if (*s == '*' || *s == '?' ||
1210 	    *s == '{' || *s == '}' ||
1211 	    *s == '[' || *s == ']' ||
1212 	    *s == '|'
1213 	) {
1214 	    return(0);
1215 	}
1216 	hv = (hv << 5) ^ *s ^ (hv >> 23);
1217 	++s;
1218     }
1219     return(((hv >> 16) ^ hv) & HMASK);
1220 }
1221 
1222 /*
1223  * WildCmp() - compare wild string to sane string
1224  *
1225  *	Return 0 on success, -1 on failure.
1226  */
1227 
1228 int
1229 WildCmp(const char *w, const char *s)
1230 {
1231     /*
1232      * skip fixed portion
1233      */
1234 
1235     for (;;) {
1236 	switch(*w) {
1237 	case '*':
1238 	    if (w[1] == 0)	/* optimize wild* case */
1239 		return(0);
1240 	    {
1241 		int i;
1242 		int l = strlen(s);
1243 
1244 		for (i = 0; i <= l; ++i) {
1245 		    if (WildCmp(w + 1, s + i) == 0)
1246 			return(0);
1247 		}
1248 	    }
1249 	    return(-1);
1250 	case '?':
1251 	    if (*s == 0)
1252 		return(-1);
1253 	    ++w;
1254 	    ++s;
1255 	    break;
1256 	default:
1257 	    if (*w != *s)
1258 		return(-1);
1259 	    if (*w == 0)	/* terminator */
1260 		return(0);
1261 	    ++w;
1262 	    ++s;
1263 	    break;
1264 	}
1265     }
1266     /* not reached */
1267     return(-1);
1268 }
1269 
1270 int
1271 YesNo(const char *path)
1272 {
1273     int ch, first;
1274 
1275     fprintf(stderr, "remove %s (Yes/No) [No]? ", path);
1276     fflush(stderr);
1277 
1278     first = ch = getchar();
1279     while (ch != '\n' && ch != EOF)
1280 	ch = getchar();
1281     return ((first == 'y' || first == 'Y'));
1282 }
1283 
1284 /*
1285  * xrename() - rename with override
1286  *
1287  *	If the rename fails, attempt to override st_flags on the
1288  *	destination and rename again.  If that fails too, try to
1289  *	set the flags back the way they were and give up.
1290  */
1291 
1292 static int
1293 xrename(const char *src, const char *dst, u_long flags)
1294 {
1295     int r;
1296 
1297     r = 0;
1298 
1299     if ((r = hc_rename(&DstHost, src, dst)) < 0) {
1300 #ifdef _ST_FLAGS_PRESENT_
1301 	hc_chflags(&DstHost, dst, 0);
1302 	if ((r = hc_rename(&DstHost, src, dst)) < 0)
1303 		hc_chflags(&DstHost, dst, flags);
1304 #endif
1305     }
1306     return(r);
1307 }
1308 
1309 static int
1310 xlink(const char *src, const char *dst, u_long flags)
1311 {
1312     int r;
1313 #ifdef _ST_FLAGS_PRESENT_
1314     int e;
1315 #endif
1316 
1317     r = 0;
1318 
1319     if ((r = hc_link(&DstHost, src, dst)) < 0) {
1320 #ifdef _ST_FLAGS_PRESENT_
1321 	hc_chflags(&DstHost, src, 0);
1322 	r = hc_link(&DstHost, src, dst);
1323 	e = errno;
1324 	hc_chflags(&DstHost, src, flags);
1325 	errno = e;
1326 #endif
1327     }
1328     return(r);
1329 }
1330 
1331