xref: /dflybsd-src/bin/cpdup/cpdup.c (revision d05b679b5aa949ff7cef967eaa12269a88d789f4)
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.17 2006/09/16 16:47:29 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 	 * If we can hardlink, and the target exists, we have to remove it
857 	 * first or the hardlink will fail.  This can occur in a number of
858 	 * situations but must typically when the '-f -H' combination is
859 	 * used.
860 	 */
861 	if (UseHLPath && (hpath = checkHLPath(&st1, spath, dpath)) != NULL) {
862 		if (st2Valid)
863 			hc_remove(&DstHost, dpath);
864 		if (hc_link(&DstHost, hpath, dpath) == 0) {
865 			if (VerboseOpt) {
866 			    logstd("%-32s hardlinked(-H)\n",
867 				   (dpath ? dpath : spath));
868 			}
869 			free(hpath);
870 			goto skip_copy;
871 		}
872 		/*
873 		 * Shucks, we may have hit a filesystem hard linking limit,
874 		 * we have to copy instead.
875 		 */
876 		free(hpath);
877 	}
878 
879 	if ((fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0)) >= 0) {
880 	    if ((fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
881 		/*
882 		 * There could be a .tmp file from a previously interrupted
883 		 * run, delete and retry.  Fail if we still can't get at it.
884 		 */
885 #ifdef _ST_FLAGS_PRESENT_
886 		hc_chflags(&DstHost, path, 0);
887 #endif
888 		hc_remove(&DstHost, path);
889 		fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
890 	    }
891 	    if (fd2 >= 0) {
892 		const char *op;
893 		int n;
894 
895 		/*
896 		 * Matt: What about holes?
897 		 */
898 		op = "read";
899 		while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
900 		    op = "write";
901 		    if (hc_write(&DstHost, fd2, IOBuf1, n) != n)
902 			break;
903 		    op = "read";
904 		}
905 		hc_close(&DstHost, fd2);
906 		if (n == 0) {
907 		    struct timeval tv[2];
908 
909 		    bzero(tv, sizeof(tv));
910 		    tv[0].tv_sec = st1.st_mtime;
911 		    tv[1].tv_sec = st1.st_mtime;
912 
913 		    hc_utimes(&DstHost, path, tv);
914 		    hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
915 		    hc_chmod(&DstHost, path, st1.st_mode);
916 		    if (xrename(path, dpath, st2.st_flags) != 0) {
917 			logerr("%-32s rename-after-copy failed: %s\n",
918 			    (dpath ? dpath : spath), strerror(errno)
919 			);
920 			++r;
921 		    } else {
922 			if (VerboseOpt)
923 			    logstd("%-32s copy-ok\n", (dpath ? dpath : spath));
924 #ifdef _ST_FLAGS_PRESENT_
925 			if (st1.st_flags)
926 			    hc_chflags(&DstHost, dpath, st1.st_flags);
927 #endif
928 		    }
929 		    CountReadBytes += size;
930 		    CountWriteBytes += size;
931 		    CountSourceBytes += size;
932 		    CountSourceItems++;
933 		    CountCopiedItems++;
934 		} else {
935 		    logerr("%-32s %s failed: %s\n",
936 			(dpath ? dpath : spath), op, strerror(errno)
937 		    );
938 		    hc_remove(&DstHost, path);
939 		    ++r;
940 		}
941 	    } else {
942 		logerr("%-32s create (uid %d, euid %d) failed: %s\n",
943 		    (dpath ? dpath : spath), getuid(), geteuid(),
944 		    strerror(errno)
945 		);
946 		++r;
947 	    }
948 	    hc_close(&SrcHost, fd1);
949 	} else {
950 	    logerr("%-32s copy: open failed: %s\n",
951 		(dpath ? dpath : spath),
952 		strerror(errno)
953 	    );
954 	    ++r;
955 	}
956 skip_copy:
957 	free(path);
958 
959         if (hln) {
960             if (!r && hc_stat(&DstHost, dpath, &st2) == 0)
961                 hln->dino = st2.st_ino;
962             else
963                 hltdelete(hln);
964         }
965     } else if (S_ISLNK(st1.st_mode)) {
966 	char link1[1024];
967 	char link2[1024];
968 	char path[2048];
969 	int n1;
970 	int n2;
971 
972 	snprintf(path, sizeof(path), "%s.tmp", dpath);
973 	n1 = hc_readlink(&SrcHost, spath, link1, sizeof(link1) - 1);
974 	n2 = hc_readlink(&DstHost, dpath, link2, sizeof(link2) - 1);
975 	if (n1 >= 0) {
976 	    if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) {
977 		hc_umask(&DstHost, ~st1.st_mode);
978 		hc_remove(&DstHost, path);
979 		link1[n1] = 0;
980 		if (hc_symlink(&DstHost, link1, path) < 0) {
981                       logerr("%-32s symlink (%s->%s) failed: %s\n",
982 			  (dpath ? dpath : spath), link1, path,
983 			  strerror(errno)
984 		      );
985 		      ++r;
986 		} else {
987 		    hc_lchown(&DstHost, path, st1.st_uid, st1.st_gid);
988 		    /*
989 		     * there is no lchmod() or lchflags(), we
990 		     * cannot chmod or chflags a softlink.
991 		     */
992 		    if (xrename(path, dpath, st2.st_flags) != 0) {
993 			logerr("%-32s rename softlink (%s->%s) failed: %s\n",
994 			    (dpath ? dpath : spath),
995 			    path, dpath, strerror(errno));
996 		    } else if (VerboseOpt) {
997 			logstd("%-32s softlink-ok\n", (dpath ? dpath : spath));
998 		    }
999 		    hc_umask(&DstHost, 000);
1000 		    CountWriteBytes += n1;
1001 		    CountCopiedItems++;
1002 	  	}
1003 	    } else {
1004 		if (VerboseOpt >= 3)
1005 		    logstd("%-32s nochange\n", (dpath ? dpath : spath));
1006 	    }
1007 	    CountSourceBytes += n1;
1008 	    CountReadBytes += n1;
1009 	    if (n2 > 0) CountReadBytes += n2;
1010 	    CountSourceItems++;
1011 	} else {
1012 	    r = 1;
1013 	    logerr("%-32s softlink-failed\n", (dpath ? dpath : spath));
1014 	}
1015     } else if (S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) {
1016 	char path[2048];
1017 
1018 	if (ForceOpt ||
1019 	    st2Valid == 0 ||
1020 	    st1.st_mode != st2.st_mode ||
1021 	    st1.st_rdev != st2.st_rdev ||
1022 	    st1.st_uid != st2.st_uid ||
1023 	    st1.st_gid != st2.st_gid
1024 	) {
1025 	    snprintf(path, sizeof(path), "%s.tmp", dpath);
1026 
1027 	    hc_remove(&DstHost, path);
1028 	    if (mknod(path, st1.st_mode, st1.st_rdev) == 0) {
1029 		hc_chmod(&DstHost, path, st1.st_mode);
1030 		hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
1031 		hc_remove(&DstHost, dpath);
1032 		if (xrename(path, dpath, st2.st_flags) != 0) {
1033 		    logerr("%-32s dev-rename-after-create failed: %s\n",
1034 			(dpath ? dpath : spath),
1035 			strerror(errno)
1036 		    );
1037 		} else if (VerboseOpt) {
1038 		    logstd("%-32s dev-ok\n", (dpath ? dpath : spath));
1039 		}
1040 		CountCopiedItems++;
1041 	    } else {
1042 		r = 1;
1043 		logerr("%-32s dev failed: %s\n",
1044 		    (dpath ? dpath : spath), strerror(errno)
1045 		);
1046 	    }
1047 	} else {
1048 	    if (VerboseOpt >= 3)
1049 		logstd("%-32s nochange\n", (dpath ? dpath : spath));
1050 	}
1051 	CountSourceItems++;
1052     }
1053     ResetList(&list);
1054     return (r);
1055 }
1056 
1057 /*
1058  * RemoveRecur()
1059  */
1060 
1061 void
1062 RemoveRecur(const char *dpath, dev_t devNo)
1063 {
1064     struct stat st;
1065 
1066     if (hc_lstat(&DstHost, dpath, &st) == 0) {
1067 	if ((int)devNo < 0)
1068 	    devNo = st.st_dev;
1069 	if (st.st_dev == devNo) {
1070 	    if (S_ISDIR(st.st_mode)) {
1071 		DIR *dir;
1072 
1073 		if ((dir = hc_opendir(&DstHost, dpath)) != NULL) {
1074 		    struct dirent *den;
1075 		    while ((den = hc_readdir(&DstHost, dir)) != NULL) {
1076 			char *ndpath;
1077 
1078 			if (strcmp(den->d_name, ".") == 0)
1079 			    continue;
1080 			if (strcmp(den->d_name, "..") == 0)
1081 			    continue;
1082 			ndpath = mprintf("%s/%s", dpath, den->d_name);
1083 			RemoveRecur(ndpath, devNo);
1084 			free(ndpath);
1085 		    }
1086 		    hc_closedir(&DstHost, dir);
1087 		}
1088 		if (AskConfirmation && NoRemoveOpt == 0) {
1089 		    if (YesNo(dpath)) {
1090 			if (hc_rmdir(&DstHost, dpath) < 0) {
1091 			    logerr("%-32s rmdir failed: %s\n",
1092 				dpath, strerror(errno)
1093 			    );
1094 			}
1095 			CountRemovedItems++;
1096 		    }
1097 		} else {
1098 		    if (NoRemoveOpt) {
1099 			if (VerboseOpt)
1100 			    logstd("%-32s not-removed\n", dpath);
1101 		    } else if (hc_rmdir(&DstHost, dpath) == 0) {
1102 			if (VerboseOpt)
1103 			    logstd("%-32s rmdir-ok\n", dpath);
1104 			CountRemovedItems++;
1105 		    } else {
1106 			logerr("%-32s rmdir failed: %s\n",
1107 			    dpath, strerror(errno)
1108 			);
1109 		    }
1110 		}
1111 	    } else {
1112 		if (AskConfirmation && NoRemoveOpt == 0) {
1113 		    if (YesNo(dpath)) {
1114 			if (hc_remove(&DstHost, dpath) < 0) {
1115 			    logerr("%-32s remove failed: %s\n",
1116 				dpath, strerror(errno)
1117 			    );
1118 			}
1119 			CountRemovedItems++;
1120 		    }
1121 		} else {
1122 		    if (NoRemoveOpt) {
1123 			if (VerboseOpt)
1124 			    logstd("%-32s not-removed\n", dpath);
1125 		    } else if (hc_remove(&DstHost, dpath) == 0) {
1126 			if (VerboseOpt)
1127 			    logstd("%-32s remove-ok\n", dpath);
1128 			CountRemovedItems++;
1129 		    } else {
1130 			logerr("%-32s remove failed: %s\n",
1131 			    dpath, strerror(errno)
1132 			);
1133 		    }
1134 		}
1135 	    }
1136 	}
1137     }
1138 }
1139 
1140 void
1141 InitList(List *list)
1142 {
1143     bzero(list, sizeof(List));
1144     list->li_Node.no_Next = &list->li_Node;
1145 }
1146 
1147 void
1148 ResetList(List *list)
1149 {
1150     Node *node;
1151 
1152     while ((node = list->li_Node.no_Next) != &list->li_Node) {
1153 	list->li_Node.no_Next = node->no_Next;
1154 	free(node);
1155     }
1156     InitList(list);
1157 }
1158 
1159 int
1160 AddList(List *list, const char *name, int n)
1161 {
1162     Node *node;
1163     int hv;
1164 
1165     hv = shash(name);
1166 
1167     /*
1168      * Scan against wildcards.  Only a node value of 1 can be a wildcard
1169      * ( usually scanned from .cpignore )
1170      */
1171 
1172     for (node = list->li_Hash[0]; node; node = node->no_HNext) {
1173 	if (strcmp(name, node->no_Name) == 0 ||
1174 	    (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0)
1175 	) {
1176 	    return(node->no_Value);
1177 	}
1178     }
1179 
1180     /*
1181      * Look for exact match
1182      */
1183 
1184     for (node = list->li_Hash[hv]; node; node = node->no_HNext) {
1185 	if (strcmp(name, node->no_Name) == 0) {
1186 	    return(node->no_Value);
1187 	}
1188     }
1189     node = malloc(sizeof(Node) + strlen(name) + 1);
1190     if (node == NULL) {
1191         fprintf(stderr, "out of memory\n");
1192         exit(EXIT_FAILURE);
1193     }
1194 
1195     node->no_Next = list->li_Node.no_Next;
1196     list->li_Node.no_Next = node;
1197 
1198     node->no_HNext = list->li_Hash[hv];
1199     list->li_Hash[hv] = node;
1200 
1201     strcpy(node->no_Name, name);
1202     node->no_Value = n;
1203 
1204     return(n);
1205 }
1206 
1207 static int
1208 shash(const char *s)
1209 {
1210     int hv;
1211 
1212     hv = 0xA4FB3255;
1213 
1214     while (*s) {
1215 	if (*s == '*' || *s == '?' ||
1216 	    *s == '{' || *s == '}' ||
1217 	    *s == '[' || *s == ']' ||
1218 	    *s == '|'
1219 	) {
1220 	    return(0);
1221 	}
1222 	hv = (hv << 5) ^ *s ^ (hv >> 23);
1223 	++s;
1224     }
1225     return(((hv >> 16) ^ hv) & HMASK);
1226 }
1227 
1228 /*
1229  * WildCmp() - compare wild string to sane string
1230  *
1231  *	Return 0 on success, -1 on failure.
1232  */
1233 
1234 int
1235 WildCmp(const char *w, const char *s)
1236 {
1237     /*
1238      * skip fixed portion
1239      */
1240 
1241     for (;;) {
1242 	switch(*w) {
1243 	case '*':
1244 	    if (w[1] == 0)	/* optimize wild* case */
1245 		return(0);
1246 	    {
1247 		int i;
1248 		int l = strlen(s);
1249 
1250 		for (i = 0; i <= l; ++i) {
1251 		    if (WildCmp(w + 1, s + i) == 0)
1252 			return(0);
1253 		}
1254 	    }
1255 	    return(-1);
1256 	case '?':
1257 	    if (*s == 0)
1258 		return(-1);
1259 	    ++w;
1260 	    ++s;
1261 	    break;
1262 	default:
1263 	    if (*w != *s)
1264 		return(-1);
1265 	    if (*w == 0)	/* terminator */
1266 		return(0);
1267 	    ++w;
1268 	    ++s;
1269 	    break;
1270 	}
1271     }
1272     /* not reached */
1273     return(-1);
1274 }
1275 
1276 int
1277 YesNo(const char *path)
1278 {
1279     int ch, first;
1280 
1281     fprintf(stderr, "remove %s (Yes/No) [No]? ", path);
1282     fflush(stderr);
1283 
1284     first = ch = getchar();
1285     while (ch != '\n' && ch != EOF)
1286 	ch = getchar();
1287     return ((first == 'y' || first == 'Y'));
1288 }
1289 
1290 /*
1291  * xrename() - rename with override
1292  *
1293  *	If the rename fails, attempt to override st_flags on the
1294  *	destination and rename again.  If that fails too, try to
1295  *	set the flags back the way they were and give up.
1296  */
1297 
1298 static int
1299 xrename(const char *src, const char *dst, u_long flags)
1300 {
1301     int r;
1302 
1303     r = 0;
1304 
1305     if ((r = hc_rename(&DstHost, src, dst)) < 0) {
1306 #ifdef _ST_FLAGS_PRESENT_
1307 	hc_chflags(&DstHost, dst, 0);
1308 	if ((r = hc_rename(&DstHost, src, dst)) < 0)
1309 		hc_chflags(&DstHost, dst, flags);
1310 #endif
1311     }
1312     return(r);
1313 }
1314 
1315 static int
1316 xlink(const char *src, const char *dst, u_long flags)
1317 {
1318     int r;
1319 #ifdef _ST_FLAGS_PRESENT_
1320     int e;
1321 #endif
1322 
1323     r = 0;
1324 
1325     if ((r = hc_link(&DstHost, src, dst)) < 0) {
1326 #ifdef _ST_FLAGS_PRESENT_
1327 	hc_chflags(&DstHost, src, 0);
1328 	r = hc_link(&DstHost, src, dst);
1329 	e = errno;
1330 	hc_chflags(&DstHost, src, flags);
1331 	errno = e;
1332 #endif
1333     }
1334     return(r);
1335 }
1336 
1337