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