Merge from vendor branch SENDMAIL:
[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.14 2006/08/14 02:41:10 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     char name[2048];
87     struct hlink *next;
88     struct hlink *prev;
89     nlink_t nlinked;
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 n;
344
345     if (!(new = malloc(sizeof (struct hlink)))) {
346         fprintf(stderr, "out of memory\n");
347         exit(EXIT_FAILURE);
348     }
349
350     /* initialize and link the new element into the table */
351     new->ino = stp->st_ino;
352     new->dino = 0;
353     strncpy(new->name, path, 2048);
354     new->nlinked = 1;
355     new->prev = NULL;
356     n = stp->st_ino & HLMASK;
357     new->next = hltable[n];
358     if (hltable[n])
359         hltable[n]->prev = new;
360     hltable[n] = new;
361
362     return new;
363 }
364
365 static void
366 hltdelete(struct hlink *hl)
367 {
368     if (hl->prev) {
369         if (hl->next)
370             hl->next->prev = hl->prev;
371         hl->prev->next = hl->next;
372     } else {
373         if (hl->next)
374             hl->next->prev = NULL;
375
376         hltable[hl->ino & HLMASK] = hl->next;
377     }
378
379     free(hl);
380 }
381
382 /*
383  * If UseHLPath is defined check to see if the file in question is
384  * the same as the source file, and if it is return a pointer to the
385  * -H path based file for hardlinking.  Else return NULL.
386  */
387 static char *
388 checkHLPath(struct stat *st1, const char *spath, const char *dpath)
389 {
390     struct stat sthl;
391     char *hpath;
392     int fd1;
393     int fd2;
394     int good;
395
396     asprintf(&hpath, "%s%s", UseHLPath, dpath + DstBaseLen);
397
398     /*
399      * stat info matches ?
400      */
401     if (hc_stat(&DstHost, hpath, &sthl) < 0 ||
402         st1->st_size != sthl.st_size ||
403         st1->st_uid != sthl.st_uid ||
404         st1->st_gid != sthl.st_gid ||
405         st1->st_mtime != sthl.st_mtime
406     ) {
407         free(hpath);
408         return(NULL);
409     }
410
411     /*
412      * If ForceOpt is set we have to compare the files
413      */
414     if (ForceOpt) {
415         fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0);
416         fd2 = hc_open(&DstHost, hpath, O_RDONLY, 0);
417         good = 0;
418
419         if (fd1 >= 0 && fd2 >= 0) {
420             int n;
421
422             while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
423                 if (hc_read(&DstHost, fd2, IOBuf2, sizeof(IOBuf2)) != n)
424                     break;
425                 if (bcmp(IOBuf1, IOBuf2, n) != 0)
426                     break;
427             }
428             if (n == 0)
429                 good = 1;
430         }
431         if (fd1 >= 0)
432             hc_close(&SrcHost, fd1);
433         if (fd2 >= 0)
434             hc_close(&DstHost, fd2);
435         if (good == 0) {
436             free(hpath);
437             hpath = NULL;
438         }
439     }
440     return(hpath);
441 }
442
443 int
444 DoCopy(const char *spath, const char *dpath, dev_t sdevNo, dev_t ddevNo)
445 {
446     struct stat st1;
447     struct stat st2;
448     int r, mres, fres, st2Valid;
449     struct hlink *hln;
450     List list;
451     u_int64_t size;
452
453     InitList(&list);
454     r = mres = fres = st2Valid = 0;
455     size = 0;
456     hln = NULL;
457
458     if (hc_lstat(&SrcHost, spath, &st1) != 0)
459         return(0);
460     st2.st_mode = 0;    /* in case lstat fails */
461     st2.st_flags = 0;   /* in case lstat fails */
462     if (dpath && hc_lstat(&DstHost, dpath, &st2) == 0)
463         st2Valid = 1;
464
465     if (S_ISREG(st1.st_mode)) {
466         size = st1.st_blocks * 512;
467         if (st1.st_size % 512) 
468             size += st1.st_size % 512 - 512;
469     }
470
471     /*
472      * Handle hardlinks
473      */
474
475     if (S_ISREG(st1.st_mode) && st1.st_nlink > 1 && dpath) {
476         if ((hln = hltlookup(&st1)) != NULL) {
477             hln->nlinked++;
478
479             if (st2Valid) {
480                 if (st2.st_ino == hln->dino) {
481                     /*
482                      * hard link is already correct, nothing to do
483                      */
484                     if (VerboseOpt >= 3)
485                         logstd("%-32s nochange\n", (dpath) ? dpath : spath);
486                     if (hln->nlinked == st1.st_nlink)
487                         hltdelete(hln);
488                     CountSourceItems++;
489                     return 0;
490                 } else {
491                     /*
492                      * hard link is not correct, attempt to unlink it
493                      */
494                     if (hc_remove(&DstHost, dpath) < 0) {
495                         logerr("%-32s hardlink: unable to unlink: %s\n", 
496                             ((dpath) ? dpath : spath), strerror(errno));
497                         hltdelete(hln);
498                         return (r + 1);
499                     }
500                 }
501             }
502
503             if (xlink(hln->name, dpath, st1.st_flags) < 0) {
504                 int tryrelink = (errno == EMLINK);
505                 logerr("%-32s hardlink: unable to link to %s: %s\n",
506                     (dpath ? dpath : spath), hln->name, strerror(errno)
507                 );
508                 hltdelete(hln);
509                 hln = NULL;
510                 if (tryrelink) {
511                     logerr("%-20s hardlink: will attempt to copy normally\n");
512                     goto relink;
513                 }
514                 ++r;
515             } else {
516                 if (hln->nlinked == st1.st_nlink) {
517                     hltdelete(hln);
518                     hln = NULL;
519                 }
520                 if (r == 0) {
521                     if (VerboseOpt) {
522                         logstd("%-32s hardlink: %s\n", 
523                             (dpath ? dpath : spath),
524                             (st2Valid ? "relinked" : "linked")
525                         );
526                     }
527                     CountSourceItems++;
528                     CountCopiedItems++;
529                     return 0;
530                 }
531             }
532         } else {
533             /*
534              * first instance of hardlink must be copied normally
535              */
536 relink:
537             hln = hltadd(&st1, dpath);
538         }
539     }
540
541     /*
542      * Do we need to copy the file/dir/link/whatever?  Early termination
543      * if we do not.  Always redo links.  Directories are always traversed
544      * except when the FSMID options are used.
545      *
546      * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good.
547      */
548
549     if (
550         st2Valid
551         && st1.st_mode == st2.st_mode
552 #ifdef _ST_FLAGS_PRESENT_
553         && st1.st_flags == st2.st_flags
554 #endif
555     ) {
556         if (S_ISLNK(st1.st_mode) || S_ISDIR(st1.st_mode)) {
557             /*
558              * If FSMID tracking is turned on we can avoid recursing through
559              * an entire directory subtree if the FSMID matches.
560              */
561 #ifdef _ST_FSMID_PRESENT_
562             if (ForceOpt == 0 &&
563                 (UseFSMIDOpt && (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
564             ) {
565                 if (VerboseOpt >= 3) {
566                     if (UseFSMIDOpt)
567                         logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
568                     else
569                         logstd("%-32s nochange\n", (dpath ? dpath : spath));
570                 }
571                 return(0);
572             }
573 #endif
574         } else {
575             if (ForceOpt == 0 &&
576                 st1.st_size == st2.st_size &&
577                 st1.st_uid == st2.st_uid &&
578                 st1.st_gid == st2.st_gid &&
579                 st1.st_mtime == st2.st_mtime
580 #ifndef NOMD5
581                 && (UseMD5Opt == 0 || (mres = md5_check(spath, dpath)) == 0)
582 #endif
583 #ifdef _ST_FSMID_PRESENT_
584                 && (UseFSMIDOpt == 0 || (fres = fsmid_check(st1.st_fsmid, dpath)) == 0)
585 #endif
586             ) {
587                 if (hln)
588                     hln->dino = st2.st_ino;
589                 if (VerboseOpt >= 3) {
590 #ifndef NOMD5
591                     if (UseMD5Opt)
592                         logstd("%-32s md5-nochange\n", (dpath ? dpath : spath));
593                     else
594 #endif
595                     if (UseFSMIDOpt)
596                         logstd("%-32s fsmid-nochange\n", (dpath ? dpath : spath));
597                     else
598                         logstd("%-32s nochange\n", (dpath ? dpath : spath));
599                 }
600                 CountSourceBytes += size;
601                 CountSourceItems++;
602
603                 return(0);
604             }
605         }
606     }
607     if (st2Valid && !S_ISDIR(st1.st_mode) && S_ISDIR(st2.st_mode)) {
608         if (SafetyOpt) {
609             logerr("%-32s SAFETY - refusing to copy file over directory\n",
610                 (dpath ? dpath : spath)
611             );
612             ++r;                /* XXX */
613             return(0);  /* continue with the cpdup anyway */
614         }
615         if (QuietOpt == 0 || AskConfirmation) {
616             logstd("%-32s WARNING: non-directory source will blow away\n"
617                    "%-32s preexisting dest directory, continuing anyway!\n",
618                    ((dpath) ? dpath : spath), "");
619         }
620         if (dpath)
621             RemoveRecur(dpath, ddevNo);
622     }
623
624     /*
625      * The various comparisons failed, copy it.
626      */
627     if (S_ISDIR(st1.st_mode)) {
628         DIR *dir;
629
630         if (fres < 0)
631             logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath);
632         if ((dir = hc_opendir(&SrcHost, spath)) != NULL) {
633             struct dirent *den;
634             int noLoop = 0;
635
636             if (dpath) {
637                 if (S_ISDIR(st2.st_mode) == 0) {
638                     hc_remove(&DstHost, dpath);
639                     if (hc_mkdir(&DstHost, dpath, st1.st_mode | 0700) != 0) {
640                         logerr("%s: mkdir failed: %s\n", 
641                             (dpath ? dpath : spath), strerror(errno));
642                         r = 1;
643                         noLoop = 1;
644                     }
645                     /*
646                      * Matt: why don't you check error codes here?
647                      */
648                     hc_lstat(&DstHost, dpath, &st2);
649                     hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid);
650                     CountCopiedItems++;
651                 } else {
652                     /*
653                      * Directory must be scanable by root for cpdup to
654                      * work.  We'll fix it later if the directory isn't
655                      * supposed to be readable ( which is why we fixup
656                      * st2.st_mode to match what we did ).
657                      */
658                     if ((st2.st_mode & 0700) != 0700) {
659                         hc_chmod(&DstHost, dpath, st2.st_mode | 0700);
660                         st2.st_mode |= 0700;
661                     }
662                     if (VerboseOpt >= 2)
663                         logstd("%s\n", dpath ? dpath : spath);
664                 }
665             }
666
667             if ((int)sdevNo >= 0 && st1.st_dev != sdevNo) {
668                 noLoop = 1;
669             } else {
670                 sdevNo = st1.st_dev;
671             }
672
673             if ((int)ddevNo >= 0 && st2.st_dev != ddevNo) {
674                 noLoop = 1;
675             } else {
676                 ddevNo = st2.st_dev;
677             }
678
679             /*
680              * scan .cpignore file for files/directories 
681              * to ignore.
682              */
683
684             if (UseCpFile) {
685                 FILE *fi;
686                 char buf[8192];
687                 char *fpath;
688
689                 if (UseCpFile[0] == '/') {
690                     fpath = mprintf("%s", UseCpFile);
691                 } else {
692                     fpath = mprintf("%s/%s", spath, UseCpFile);
693                 }
694                 AddList(&list, strrchr(fpath, '/') + 1, 1);
695                 if ((fi = fopen(fpath, "r")) != NULL) {
696                     while (fgets(buf, sizeof(buf), fi) != NULL) {
697                         int l = strlen(buf);
698                         CountReadBytes += l;
699                         if (l && buf[l-1] == '\n')
700                             buf[--l] = 0;
701                         if (buf[0] && buf[0] != '#')
702                             AddList(&list, buf, 1);
703                     }
704                     fclose(fi);
705                 }
706                 free(fpath);
707             }
708
709             /*
710              * Automatically exclude MD5CacheFile that we create on the
711              * source from the copy to the destination.
712              *
713              * Automatically exclude a FSMIDCacheFile on the source that
714              * would otherwise overwrite the one we maintain on the target.
715              */
716             if (UseMD5Opt)
717                 AddList(&list, MD5CacheFile, 1);
718             if (UseFSMIDOpt)
719                 AddList(&list, FSMIDCacheFile, 1);
720
721             while (noLoop == 0 && (den = hc_readdir(&SrcHost, dir)) != NULL) {
722                 /*
723                  * ignore . and ..
724                  */
725                 char *nspath;
726                 char *ndpath = NULL;
727
728                 if (strcmp(den->d_name, ".") == 0 ||
729                     strcmp(den->d_name, "..") == 0
730                 ) {
731                     continue;
732                 }
733                 /*
734                  * ignore if on .cpignore list
735                  */
736                 if (AddList(&list, den->d_name, 0) == 1) {
737                     continue;
738                 }
739                 nspath = mprintf("%s/%s", spath, den->d_name);
740                 if (dpath)
741                     ndpath = mprintf("%s/%s", dpath, den->d_name);
742                 r += DoCopy(
743                     nspath,
744                     ndpath,
745                     sdevNo,
746                     ddevNo
747                 );
748                 free(nspath);
749                 if (ndpath)
750                     free(ndpath);
751             }
752
753             hc_closedir(&SrcHost, dir);
754
755             /*
756              * Remove files/directories from destination that do not appear
757              * in the source.
758              */
759             if (dpath && (dir = hc_opendir(&DstHost, dpath)) != NULL) {
760                 while (noLoop == 0 && (den = hc_readdir(&DstHost, dir)) != NULL) {
761                     /*
762                      * ignore . or ..
763                      */
764                     if (strcmp(den->d_name, ".") == 0 ||
765                         strcmp(den->d_name, "..") == 0
766                     ) {
767                         continue;
768                     }
769                     /*
770                      * If object does not exist in source or .cpignore
771                      * then recursively remove it.
772                      */
773                     if (AddList(&list, den->d_name, 3) == 3) {
774                         char *ndpath;
775
776                         ndpath = mprintf("%s/%s", dpath, den->d_name);
777                         RemoveRecur(ndpath, ddevNo);
778                         free(ndpath);
779                     }
780                 }
781                 hc_closedir(&DstHost, dir);
782             }
783
784             if (dpath) {
785                 if (ForceOpt ||
786                     st2Valid == 0 || 
787                     st1.st_uid != st2.st_uid ||
788                     st1.st_gid != st2.st_gid
789                 ) {
790                     hc_chown(&DstHost, dpath, st1.st_uid, st1.st_gid);
791                 }
792                 if (st2Valid == 0 || st1.st_mode != st2.st_mode) {
793                     hc_chmod(&DstHost, dpath, st1.st_mode);
794                 }
795 #ifdef _ST_FLAGS_PRESENT_
796                 if (st2Valid == 0 || st1.st_flags != st2.st_flags) {
797                     hc_chflags(&DstHost, dpath, st1.st_flags);
798                 }
799 #endif
800             }
801         }
802     } else if (dpath == NULL) {
803         /*
804          * If dpath is NULL, we are just updating the MD5
805          */
806 #ifndef NOMD5
807         if (UseMD5Opt && S_ISREG(st1.st_mode)) {
808             mres = md5_check(spath, NULL);
809
810             if (VerboseOpt > 1) {
811                 if (mres < 0)
812                     logstd("%-32s md5-update\n", (dpath) ? dpath : spath);
813                 else
814                     logstd("%-32s md5-ok\n", (dpath) ? dpath : spath);
815             } else if (!QuietOpt && mres < 0) {
816                 logstd("%-32s md5-update\n", (dpath) ? dpath : spath);
817             }
818         }
819 #endif
820     } else if (S_ISREG(st1.st_mode)) {
821         char *path;
822         char *hpath;
823         int fd1;
824         int fd2;
825
826         path = mprintf("%s.tmp", dpath);
827
828         /*
829          * Handle check failure message.
830          */
831 #ifndef NOMD5
832         if (mres < 0)
833             logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath);
834         else 
835 #endif
836         if (fres < 0)
837             logerr("%-32s fsmid-CHECK-FAILED\n", (dpath) ? dpath : spath);
838
839         /*
840          * Not quite ready to do the copy yet.  If UseHLPath is defined,
841          * see if we can hardlink instead.
842          */
843
844         if (UseHLPath && (hpath = checkHLPath(&st1, spath, dpath)) != NULL) {
845                 if (hc_link(&DstHost, hpath, dpath) == 0) {
846                         if (VerboseOpt) {
847                             logstd("%-32s hardlinked(-H)\n",
848                                    (dpath ? dpath : spath));
849                         }
850                         free(hpath);
851                         goto skip_copy;
852                 }
853                 /*
854                  * Shucks, we may have hit a filesystem hard linking limit,
855                  * we have to copy instead.
856                  */
857                 free(hpath);
858         }
859
860         if ((fd1 = hc_open(&SrcHost, spath, O_RDONLY, 0)) >= 0) {
861             if ((fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
862                 /*
863                  * There could be a .tmp file from a previously interrupted
864                  * run, delete and retry.  Fail if we still can't get at it.
865                  */
866 #ifdef _ST_FLAGS_PRESENT_
867                 hc_chflags(&DstHost, path, 0);
868 #endif
869                 hc_remove(&DstHost, path);
870                 fd2 = hc_open(&DstHost, path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
871             }
872             if (fd2 >= 0) {
873                 const char *op;
874                 int n;
875
876                 /*
877                  * Matt: What about holes?
878                  */
879                 op = "read";
880                 while ((n = hc_read(&SrcHost, fd1, IOBuf1, sizeof(IOBuf1))) > 0) {
881                     op = "write";
882                     if (hc_write(&DstHost, fd2, IOBuf1, n) != n)
883                         break;
884                     op = "read";
885                 }
886                 hc_close(&DstHost, fd2);
887                 if (n == 0) {
888                     struct timeval tv[2];
889
890                     bzero(tv, sizeof(tv));
891                     tv[0].tv_sec = st1.st_mtime;
892                     tv[1].tv_sec = st1.st_mtime;
893
894                     hc_utimes(&DstHost, path, tv);
895                     hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
896                     hc_chmod(&DstHost, path, st1.st_mode);
897                     if (xrename(path, dpath, st2.st_flags) != 0) {
898                         logerr("%-32s rename-after-copy failed: %s\n",
899                             (dpath ? dpath : spath), strerror(errno)
900                         );
901                         ++r;
902                     } else {
903                         if (VerboseOpt)
904                             logstd("%-32s copy-ok\n", (dpath ? dpath : spath));
905 #ifdef _ST_FLAGS_PRESENT_
906                         if (st1.st_flags)
907                             hc_chflags(&DstHost, dpath, st1.st_flags);
908 #endif
909                     }
910                     CountReadBytes += size;
911                     CountWriteBytes += size;
912                     CountSourceBytes += size;
913                     CountSourceItems++;
914                     CountCopiedItems++;
915                 } else {
916                     logerr("%-32s %s failed: %s\n",
917                         (dpath ? dpath : spath), op, strerror(errno)
918                     );
919                     hc_remove(&DstHost, path);
920                     ++r;
921                 }
922             } else {
923                 logerr("%-32s create (uid %d, euid %d) failed: %s\n",
924                     (dpath ? dpath : spath), getuid(), geteuid(),
925                     strerror(errno)
926                 );
927                 ++r;
928             }
929             hc_close(&SrcHost, fd1);
930         } else {
931             logerr("%-32s copy: open failed: %s\n",
932                 (dpath ? dpath : spath),
933                 strerror(errno)
934             );
935             ++r;
936         }
937 skip_copy:
938         free(path);
939
940         if (hln) {
941             if (!r && hc_stat(&DstHost, dpath, &st2) == 0)
942                 hln->dino = st2.st_ino;
943             else
944                 hltdelete(hln);
945         }
946     } else if (S_ISLNK(st1.st_mode)) {
947         char link1[1024];
948         char link2[1024];
949         char path[2048];
950         int n1;
951         int n2;
952
953         snprintf(path, sizeof(path), "%s.tmp", dpath);
954         n1 = hc_readlink(&SrcHost, spath, link1, sizeof(link1) - 1);
955         n2 = hc_readlink(&DstHost, dpath, link2, sizeof(link2) - 1);
956         if (n1 >= 0) {
957             if (ForceOpt || n1 != n2 || bcmp(link1, link2, n1) != 0) {
958                 hc_umask(&DstHost, ~st1.st_mode);
959                 hc_remove(&DstHost, path);
960                 link1[n1] = 0;
961                 if (hc_symlink(&DstHost, link1, path) < 0) {
962                       logerr("%-32s symlink (%s->%s) failed: %s\n",
963                           (dpath ? dpath : spath), link1, path,
964                           strerror(errno)
965                       );
966                       ++r;
967                 } else {
968                     hc_lchown(&DstHost, path, st1.st_uid, st1.st_gid);
969                     /*
970                      * there is no lchmod() or lchflags(), we 
971                      * cannot chmod or chflags a softlink.
972                      */
973                     if (xrename(path, dpath, st2.st_flags) != 0) {
974                         logerr("%-32s rename softlink (%s->%s) failed: %s\n",
975                             (dpath ? dpath : spath),
976                             path, dpath, strerror(errno));
977                     } else if (VerboseOpt) {
978                         logstd("%-32s softlink-ok\n", (dpath ? dpath : spath));
979                     }
980                     hc_umask(&DstHost, 000);
981                     CountWriteBytes += n1;
982                     CountCopiedItems++;
983                 }
984             } else {
985                 if (VerboseOpt >= 3)
986                     logstd("%-32s nochange\n", (dpath ? dpath : spath));
987             }
988             CountSourceBytes += n1;
989             CountReadBytes += n1;
990             if (n2 > 0) CountReadBytes += n2;
991             CountSourceItems++;
992         } else {
993             r = 1;
994             logerr("%-32s softlink-failed\n", (dpath ? dpath : spath));
995         }
996     } else if (S_ISCHR(st1.st_mode) || S_ISBLK(st1.st_mode)) {
997         char path[2048];
998
999         if (ForceOpt ||
1000             st2Valid == 0 || 
1001             st1.st_mode != st2.st_mode || 
1002             st1.st_rdev != st2.st_rdev ||
1003             st1.st_uid != st2.st_uid ||
1004             st1.st_gid != st2.st_gid
1005         ) {
1006             snprintf(path, sizeof(path), "%s.tmp", dpath);
1007
1008             hc_remove(&DstHost, path);
1009             if (mknod(path, st1.st_mode, st1.st_rdev) == 0) {
1010                 hc_chmod(&DstHost, path, st1.st_mode);
1011                 hc_chown(&DstHost, path, st1.st_uid, st1.st_gid);
1012                 hc_remove(&DstHost, dpath);
1013                 if (xrename(path, dpath, st2.st_flags) != 0) {
1014                     logerr("%-32s dev-rename-after-create failed: %s\n",
1015                         (dpath ? dpath : spath),
1016                         strerror(errno)
1017                     );
1018                 } else if (VerboseOpt) {
1019                     logstd("%-32s dev-ok\n", (dpath ? dpath : spath));
1020                 }
1021                 CountCopiedItems++;
1022             } else {
1023                 r = 1;
1024                 logerr("%-32s dev failed: %s\n", 
1025                     (dpath ? dpath : spath), strerror(errno)
1026                 );
1027             }
1028         } else {
1029             if (VerboseOpt >= 3)
1030                 logstd("%-32s nochange\n", (dpath ? dpath : spath));
1031         }
1032         CountSourceItems++;
1033     }
1034     ResetList(&list);
1035     return (r);
1036 }
1037
1038 /*
1039  * RemoveRecur()
1040  */
1041
1042 void
1043 RemoveRecur(const char *dpath, dev_t devNo)
1044 {
1045     struct stat st;
1046
1047     if (hc_lstat(&DstHost, dpath, &st) == 0) {
1048         if ((int)devNo < 0)
1049             devNo = st.st_dev;
1050         if (st.st_dev == devNo) {
1051             if (S_ISDIR(st.st_mode)) {
1052                 DIR *dir;
1053
1054                 if ((dir = hc_opendir(&DstHost, dpath)) != NULL) {
1055                     struct dirent *den;
1056                     while ((den = hc_readdir(&DstHost, dir)) != NULL) {
1057                         char *ndpath;
1058
1059                         if (strcmp(den->d_name, ".") == 0)
1060                             continue;
1061                         if (strcmp(den->d_name, "..") == 0)
1062                             continue;
1063                         ndpath = mprintf("%s/%s", dpath, den->d_name);
1064                         RemoveRecur(ndpath, devNo);
1065                         free(ndpath);
1066                     }
1067                     hc_closedir(&DstHost, dir);
1068                 }
1069                 if (AskConfirmation && NoRemoveOpt == 0) {
1070                     if (YesNo(dpath)) {
1071                         if (hc_rmdir(&DstHost, dpath) < 0) {
1072                             logerr("%-32s rmdir failed: %s\n",
1073                                 dpath, strerror(errno)
1074                             );
1075                         }
1076                         CountRemovedItems++;
1077                     }
1078                 } else {
1079                     if (NoRemoveOpt) {
1080                         if (VerboseOpt)
1081                             logstd("%-32s not-removed\n", dpath);
1082                     } else if (hc_rmdir(&DstHost, dpath) == 0) {
1083                         if (VerboseOpt)
1084                             logstd("%-32s rmdir-ok\n", dpath);
1085                         CountRemovedItems++;
1086                     } else {
1087                         logerr("%-32s rmdir failed: %s\n",
1088                             dpath, strerror(errno)
1089                         );
1090                     }
1091                 }
1092             } else {
1093                 if (AskConfirmation && NoRemoveOpt == 0) {
1094                     if (YesNo(dpath)) {
1095                         if (hc_remove(&DstHost, dpath) < 0) {
1096                             logerr("%-32s remove failed: %s\n",
1097                                 dpath, strerror(errno)
1098                             );
1099                         }
1100                         CountRemovedItems++;
1101                     }
1102                 } else {
1103                     if (NoRemoveOpt) {
1104                         if (VerboseOpt)
1105                             logstd("%-32s not-removed\n", dpath);
1106                     } else if (hc_remove(&DstHost, dpath) == 0) {
1107                         if (VerboseOpt)
1108                             logstd("%-32s remove-ok\n", dpath);
1109                         CountRemovedItems++;
1110                     } else {
1111                         logerr("%-32s remove failed: %s\n",
1112                             dpath, strerror(errno)
1113                         );
1114                     }
1115                 }
1116             }
1117         }
1118     }
1119 }
1120
1121 void
1122 InitList(List *list)
1123 {
1124     bzero(list, sizeof(List));
1125     list->li_Node.no_Next = &list->li_Node;
1126 }
1127
1128 void 
1129 ResetList(List *list)
1130 {
1131     Node *node;
1132
1133     while ((node = list->li_Node.no_Next) != &list->li_Node) {
1134         list->li_Node.no_Next = node->no_Next;
1135         free(node);
1136     }
1137     InitList(list);
1138 }
1139
1140 int
1141 AddList(List *list, const char *name, int n)
1142 {
1143     Node *node;
1144     int hv;
1145
1146     hv = shash(name);
1147
1148     /*
1149      * Scan against wildcards.  Only a node value of 1 can be a wildcard
1150      * ( usually scanned from .cpignore )
1151      */
1152
1153     for (node = list->li_Hash[0]; node; node = node->no_HNext) {
1154         if (strcmp(name, node->no_Name) == 0 ||
1155             (n != 1 && node->no_Value == 1 && WildCmp(node->no_Name, name) == 0)
1156         ) {
1157             return(node->no_Value);
1158         }
1159     }
1160
1161     /*
1162      * Look for exact match
1163      */
1164
1165     for (node = list->li_Hash[hv]; node; node = node->no_HNext) {
1166         if (strcmp(name, node->no_Name) == 0) {
1167             return(node->no_Value);
1168         }
1169     }
1170     node = malloc(sizeof(Node) + strlen(name) + 1);
1171     if (node == NULL) {
1172         fprintf(stderr, "out of memory\n");
1173         exit(EXIT_FAILURE);
1174     }
1175
1176     node->no_Next = list->li_Node.no_Next;
1177     list->li_Node.no_Next = node;
1178
1179     node->no_HNext = list->li_Hash[hv];
1180     list->li_Hash[hv] = node;
1181
1182     strcpy(node->no_Name, name);
1183     node->no_Value = n;
1184
1185     return(n);
1186 }
1187
1188 static int
1189 shash(const char *s)
1190 {
1191     int hv;
1192
1193     hv = 0xA4FB3255;
1194
1195     while (*s) {
1196         if (*s == '*' || *s == '?' || 
1197             *s == '{' || *s == '}' || 
1198             *s == '[' || *s == ']' ||
1199             *s == '|'
1200         ) {
1201             return(0);
1202         }
1203         hv = (hv << 5) ^ *s ^ (hv >> 23);
1204         ++s;
1205     }
1206     return(((hv >> 16) ^ hv) & HMASK);
1207 }
1208
1209 /*
1210  * WildCmp() - compare wild string to sane string
1211  *
1212  *      Return 0 on success, -1 on failure.
1213  */
1214
1215 int
1216 WildCmp(const char *w, const char *s)
1217 {
1218     /*
1219      * skip fixed portion
1220      */
1221   
1222     for (;;) {
1223         switch(*w) {
1224         case '*':
1225             if (w[1] == 0)      /* optimize wild* case */
1226                 return(0);
1227             {
1228                 int i;
1229                 int l = strlen(s);
1230
1231                 for (i = 0; i <= l; ++i) {
1232                     if (WildCmp(w + 1, s + i) == 0)
1233                         return(0);
1234                 }
1235             }
1236             return(-1);
1237         case '?':
1238             if (*s == 0)
1239                 return(-1);
1240             ++w;
1241             ++s;
1242             break;
1243         default:
1244             if (*w != *s)
1245                 return(-1);
1246             if (*w == 0)        /* terminator */
1247                 return(0);
1248             ++w;
1249             ++s;
1250             break;
1251         }
1252     }
1253     /* not reached */
1254     return(-1);
1255 }
1256
1257 int
1258 YesNo(const char *path)
1259 {
1260     int ch, first;
1261
1262     fprintf(stderr, "remove %s (Yes/No) [No]? ", path);
1263     fflush(stderr);
1264
1265     first = ch = getchar();
1266     while (ch != '\n' && ch != EOF)
1267         ch = getchar();
1268     return ((first == 'y' || first == 'Y'));
1269 }
1270
1271 /*
1272  * xrename() - rename with override
1273  *
1274  *      If the rename fails, attempt to override st_flags on the 
1275  *      destination and rename again.  If that fails too, try to
1276  *      set the flags back the way they were and give up.
1277  */
1278
1279 static int
1280 xrename(const char *src, const char *dst, u_long flags)
1281 {
1282     int r;
1283
1284     r = 0;
1285
1286     if ((r = hc_rename(&DstHost, src, dst)) < 0) {
1287 #ifdef _ST_FLAGS_PRESENT_
1288         hc_chflags(&DstHost, dst, 0);
1289         if ((r = hc_rename(&DstHost, src, dst)) < 0)
1290                 hc_chflags(&DstHost, dst, flags);
1291 #endif
1292     }
1293     return(r);
1294 }
1295
1296 static int
1297 xlink(const char *src, const char *dst, u_long flags)
1298 {
1299     int r;
1300 #ifdef _ST_FLAGS_PRESENT_
1301     int e;
1302 #endif
1303
1304     r = 0;
1305
1306     if ((r = hc_link(&DstHost, src, dst)) < 0) {
1307 #ifdef _ST_FLAGS_PRESENT_
1308         hc_chflags(&DstHost, src, 0);
1309         r = hc_link(&DstHost, src, dst);
1310         e = errno;
1311         hc_chflags(&DstHost, src, flags);
1312         errno = e;
1313 #endif
1314     }
1315     return(r);
1316 }
1317