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