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