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