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