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