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