xref: /dflybsd-src/bin/cpdup/cpdup.c (revision a9cfaf7ce72f185c8b4450485c9415aa0c648bfe)
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 
1233