9f3422e0f03a0f25112077b693135eedbdeacaef
[dragonfly.git] / bin / cpdup / cpdup.c
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