4 * CPDUP <options> source destination
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.
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.
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
28 * - does not copy file if mtime, flags, perms, and size match unless
31 * - copies to temporary and renames-over the original, allowing
32 * you to update live systems
34 * - copies uid, gid, mtime, perms, flags, softlinks, devices, hardlinks,
35 * and recurses through directories.
37 * - accesses a per-directory exclusion file, .cpignore, containing
38 * standard wildcarded ( ? / * style, NOT regex) exclusions.
40 * - tries to play permissions and flags smart in regards to overwriting
41 * schg files and doing related stuff.
43 * - Can do MD5 consistancy checks
45 * - Is able to do incremental mirroring/backups via hardlinks from
46 * the 'previous' version (supplied with -H path).
48 * $DragonFly: src/bin/cpdup/cpdup.c,v 1.18 2006/09/21 04:09:28 dillon Exp $
52 * Example: cc -O cpdup.c -o cpdup -lmd
54 * ".MD5.CHECKSUMS" contains md5 checksumms for the current directory.
55 * This file is stored on the source.
63 #define HMASK (HSIZE-1)
65 #define HLMASK (HLSIZE - 1)
67 #ifndef _ST_FLAGS_PRESENT_
68 #define st_flags st_mode
73 struct Node
*no_HNext
;
92 struct hlink
*hltable
[HLSIZE
];
94 void RemoveRecur(const char *dpath
, dev_t devNo
);
95 void InitList(List
*list
);
96 void ResetList(List
*list
);
97 int AddList(List
*list
, const char *name
, int n
);
98 static struct hlink
*hltlookup(struct stat
*);
99 static struct hlink
*hltadd(struct stat
*, const char *);
100 static char *checkHLPath(struct stat
*st
, const char *spath
, const char *dpath
);
101 static int shash(const char *s
);
102 static void hltdelete(struct hlink
*);
103 int YesNo(const char *path
);
104 static int xrename(const char *src
, const char *dst
, u_long flags
);
105 static int xlink(const char *src
, const char *dst
, u_long flags
);
106 int WildCmp(const char *s1
, const char *s2
);
107 int DoCopy(const char *spath
, const char *dpath
, dev_t sdevNo
, dev_t ddevNo
);
109 int AskConfirmation
= 1;
120 int EnableDirectoryRetries
;
124 const char *UseCpFile
;
125 const char *UseHLPath
;
126 const char *MD5CacheFile
;
127 const char *FSMIDCacheFile
;
129 int64_t CountSourceBytes
;
130 int64_t CountSourceItems
;
131 int64_t CountCopiedItems
;
132 int64_t CountReadBytes
;
133 int64_t CountWriteBytes
;
134 int64_t CountRemovedItems
;
136 struct HostConf SrcHost
;
137 struct HostConf DstHost
;
140 main(int ac
, char **av
)
146 struct timeval start
;
148 signal(SIGPIPE
, SIG_IGN
);
150 gettimeofday(&start
, NULL
);
151 for (i
= 1; i
< ac
; ++i
) {
158 } else if (dst
== NULL
) {
161 fatal("too many arguments");
169 v
= strtol(ptr
, NULL
, 0);
174 while (*ptr
== 'v') {
178 if (*ptr
>= '0' && *ptr
<= '9')
179 VerboseOpt
= strtol(ptr
, NULL
, 0);
188 UseCpFile
= ".cpignore";
191 UseCpFile
= (*ptr
) ? ptr
: av
[++i
];
194 UseHLPath
= (*ptr
) ? ptr
: av
[++i
];
216 FSMIDCacheFile
= ".FSMID.CHECK";
220 FSMIDCacheFile
= av
[++i
];
224 MD5CacheFile
= av
[++i
];
228 MD5CacheFile
= ".MD5.CHECKSUMS";
231 setvbuf(stdout
, NULL
, _IOLBF
, 0);
234 fatal("illegal option: %s\n", ptr
- 2);
241 * If we are told to go into slave mode, run the HC protocol
249 * Extract the source and/or/neither target [user@]host and
250 * make any required connections.
252 if (src
&& (ptr
= strchr(src
, ':')) != NULL
) {
253 asprintf(&SrcHost
.host
, "%*.*s", ptr
- src
, ptr
- src
, src
);
256 fprintf(stderr
, "The cpignore options are not currently supported for remote sources\n");
260 fprintf(stderr
, "The MD5 options are not currently supported for remote sources\n");
263 if (hc_connect(&SrcHost
) < 0)
264 fprintf(stderr
, "Unable to connect to %s\n", SrcHost
.host
);
266 if (dst
&& (ptr
= strchr(dst
, ':')) != NULL
) {
267 asprintf(&DstHost
.host
, "%*.*s", ptr
- dst
, ptr
- dst
, dst
);
270 fprintf(stderr
, "The FSMID options are not currently supported for remote targets\n");
273 if (hc_connect(&DstHost
) < 0)
274 fprintf(stderr
, "Unable to connect to %s\n", DstHost
.host
);
278 * dst may be NULL only if -m option is specified,
279 * which forces an update of the MD5 checksums
281 if (dst
== NULL
&& UseMD5Opt
== 0) {
286 DstBaseLen
= strlen(dst
);
287 i
= DoCopy(src
, dst
, (dev_t
)-1, (dev_t
)-1);
289 i
= DoCopy(src
, NULL
, (dev_t
)-1, (dev_t
)-1);
296 if (SummaryOpt
&& i
== 0) {
300 gettimeofday(&end
, NULL
);
301 CountSourceBytes
+= sizeof(struct stat
) * CountSourceItems
;
302 CountReadBytes
+= sizeof(struct stat
) * CountSourceItems
;
303 CountWriteBytes
+= sizeof(struct stat
) * CountCopiedItems
;
304 CountWriteBytes
+= sizeof(struct stat
) * CountRemovedItems
;
306 duration
= end
.tv_sec
- start
.tv_sec
;
308 duration
+= end
.tv_usec
- start
.tv_usec
;
309 if (duration
== 0) duration
= 1;
310 logstd("cpdup completed successfully\n");
311 logstd("%lld bytes source %lld bytes read %lld bytes written (%.1fX speedup)\n",
312 (long long)CountSourceBytes
,
313 (long long)CountReadBytes
,
314 (long long)CountWriteBytes
,
315 ((double)CountSourceBytes
* 2.0) / ((double)(CountReadBytes
+ CountWriteBytes
)));
316 logstd("%lld source items %lld items copied %lld things deleted\n",
317 (long long)CountSourceItems
,
318 (long long)CountCopiedItems
,
319 (long long)CountRemovedItems
);
320 logstd("%.1f seconds %5d Kbytes/sec synced %5d Kbytes/sec scanned\n",
321 (float)duration
/ (float)1000000,
322 (long)((long)1000000 * (CountReadBytes
+ CountWriteBytes
) / duration
/ 1024.0),
323 (long)((long)1000000 * CountSourceBytes
/ duration
/ 1024.0));
325 exit((i
== 0) ? 0 : 1);
328 static struct hlink
*
329 hltlookup(struct stat
*stp
)
334 n
= stp
->st_ino
& HLMASK
;
336 for (hl
= hltable
[n
]; hl
; hl
= hl
->next
)
337 if (hl
->ino
== stp
->st_ino
)
343 static struct hlink
*
344 hltadd(struct stat
*stp
, const char *path
)
347 int plen
= strlen(path
);
350 new = malloc(offsetof(struct hlink
, name
[plen
+ 1]));
352 fprintf(stderr
, "out of memory\n");
356 /* initialize and link the new element into the table */
357 new->ino
= stp
->st_ino
;
359 bcopy(path
, new->name
, plen
+ 1);
362 n
= stp
->st_ino
& HLMASK
;
363 new->next
= hltable
[n
];
365 hltable
[n
]->prev
= new;
372 hltdelete(struct hlink
*hl
)
376 hl
->next
->prev
= hl
->prev
;
377 hl
->prev
->next
= hl
->next
;
380 hl
->next
->prev
= NULL
;
382 hltable
[hl
->ino
& HLMASK
] = hl
->next
;
389 * If UseHLPath is defined check to see if the file in question is
390 * the same as the source file, and if it is return a pointer to the
391 * -H path based file for hardlinking. Else return NULL.
394 checkHLPath(struct stat
*st1
, const char *spath
, const char *dpath
)
402 asprintf(&hpath
, "%s%s", UseHLPath
, dpath
+ DstBaseLen
);
405 * stat info matches ?
407 if (hc_stat(&DstHost
, hpath
, &sthl
) < 0 ||
408 st1
->st_size
!= sthl
.st_size
||
409 st1
->st_uid
!= sthl
.st_uid
||
410 st1
->st_gid
!= sthl
.st_gid
||
411 st1
->st_mtime
!= sthl
.st_mtime
418 * If ForceOpt is set we have to compare the files
421 fd1
= hc_open(&SrcHost
, spath
, O_RDONLY
, 0);
422 fd2
= hc_open(&DstHost
, hpath
, O_RDONLY
, 0);
425 if (fd1
>= 0 && fd2
>= 0) {
428 while ((n
= hc_read(&SrcHost
, fd1
, IOBuf1
, sizeof(IOBuf1
))) > 0) {
429 if (hc_read(&DstHost
, fd2
, IOBuf2
, sizeof(IOBuf2
)) != n
)
431 if (bcmp(IOBuf1
, IOBuf2
, n
) != 0)
438 hc_close(&SrcHost
, fd1
);
440 hc_close(&DstHost
, fd2
);
450 DoCopy(const char *spath
, const char *dpath
, dev_t sdevNo
, dev_t ddevNo
)
454 int r
, mres
, fres
, st2Valid
;
460 r
= mres
= fres
= st2Valid
= 0;
464 if (hc_lstat(&SrcHost
, spath
, &st1
) != 0)
466 st2
.st_mode
= 0; /* in case lstat fails */
467 st2
.st_flags
= 0; /* in case lstat fails */
468 if (dpath
&& hc_lstat(&DstHost
, dpath
, &st2
) == 0)
471 if (S_ISREG(st1
.st_mode
)) {
472 size
= st1
.st_blocks
* 512;
473 if (st1
.st_size
% 512)
474 size
+= st1
.st_size
% 512 - 512;
481 if (S_ISREG(st1
.st_mode
) && st1
.st_nlink
> 1 && dpath
) {
482 if ((hln
= hltlookup(&st1
)) != NULL
) {
486 if (st2
.st_ino
== hln
->dino
) {
488 * hard link is already correct, nothing to do
491 logstd("%-32s nochange\n", (dpath
) ? dpath
: spath
);
492 if (hln
->nlinked
== st1
.st_nlink
)
498 * hard link is not correct, attempt to unlink it
500 if (hc_remove(&DstHost
, dpath
) < 0) {
501 logerr("%-32s hardlink: unable to unlink: %s\n",
502 ((dpath
) ? dpath
: spath
), strerror(errno
));
509 if (xlink(hln
->name
, dpath
, st1
.st_flags
) < 0) {
510 int tryrelink
= (errno
== EMLINK
);
511 logerr("%-32s hardlink: unable to link to %s: %s\n",
512 (dpath
? dpath
: spath
), hln
->name
, strerror(errno
)
517 logerr("%-20s hardlink: will attempt to copy normally\n");
522 if (hln
->nlinked
== st1
.st_nlink
) {
528 logstd("%-32s hardlink: %s\n",
529 (dpath
? dpath
: spath
),
530 (st2Valid
? "relinked" : "linked")
540 * first instance of hardlink must be copied normally
543 hln
= hltadd(&st1
, dpath
);
548 * Do we need to copy the file/dir/link/whatever? Early termination
549 * if we do not. Always redo links. Directories are always traversed
550 * except when the FSMID options are used.
552 * NOTE: st2Valid is true only if dpath != NULL *and* dpath stats good.
557 && st1
.st_mode
== st2
.st_mode
558 #ifdef _ST_FLAGS_PRESENT_
559 && st1
.st_flags
== st2
.st_flags
562 if (S_ISLNK(st1
.st_mode
) || S_ISDIR(st1
.st_mode
)) {
564 * If FSMID tracking is turned on we can avoid recursing through
565 * an entire directory subtree if the FSMID matches.
567 #ifdef _ST_FSMID_PRESENT_
569 (UseFSMIDOpt
&& (fres
= fsmid_check(st1
.st_fsmid
, dpath
)) == 0)
571 if (VerboseOpt
>= 3) {
573 logstd("%-32s fsmid-nochange\n", (dpath
? dpath
: spath
));
575 logstd("%-32s nochange\n", (dpath
? dpath
: spath
));
582 st1
.st_size
== st2
.st_size
&&
583 st1
.st_uid
== st2
.st_uid
&&
584 st1
.st_gid
== st2
.st_gid
&&
585 st1
.st_mtime
== st2
.st_mtime
587 && (UseMD5Opt
== 0 || (mres
= md5_check(spath
, dpath
)) == 0)
589 #ifdef _ST_FSMID_PRESENT_
590 && (UseFSMIDOpt
== 0 || (fres
= fsmid_check(st1
.st_fsmid
, dpath
)) == 0)
594 hln
->dino
= st2
.st_ino
;
595 if (VerboseOpt
>= 3) {
598 logstd("%-32s md5-nochange\n", (dpath
? dpath
: spath
));
602 logstd("%-32s fsmid-nochange\n", (dpath
? dpath
: spath
));
604 logstd("%-32s nochange\n", (dpath
? dpath
: spath
));
606 CountSourceBytes
+= size
;
613 if (st2Valid
&& !S_ISDIR(st1
.st_mode
) && S_ISDIR(st2
.st_mode
)) {
615 logerr("%-32s SAFETY - refusing to copy file over directory\n",
616 (dpath
? dpath
: spath
)
619 return(0); /* continue with the cpdup anyway */
621 if (QuietOpt
== 0 || AskConfirmation
) {
622 logstd("%-32s WARNING: non-directory source will blow away\n"
623 "%-32s preexisting dest directory, continuing anyway!\n",
624 ((dpath
) ? dpath
: spath
), "");
627 RemoveRecur(dpath
, ddevNo
);
631 * The various comparisons failed, copy it.
633 if (S_ISDIR(st1
.st_mode
)) {
637 logerr("%-32s/ fsmid-CHECK-FAILED\n", (dpath
) ? dpath
: spath
);
638 if ((dir
= hc_opendir(&SrcHost
, spath
)) != NULL
) {
643 if (S_ISDIR(st2
.st_mode
) == 0) {
644 hc_remove(&DstHost
, dpath
);
645 if (hc_mkdir(&DstHost
, dpath
, st1
.st_mode
| 0700) != 0) {
646 logerr("%s: mkdir failed: %s\n",
647 (dpath
? dpath
: spath
), strerror(errno
));
652 * Matt: why don't you check error codes here?
654 hc_lstat(&DstHost
, dpath
, &st2
);
655 hc_chown(&DstHost
, dpath
, st1
.st_uid
, st1
.st_gid
);
659 * Directory must be scanable by root for cpdup to
660 * work. We'll fix it later if the directory isn't
661 * supposed to be readable ( which is why we fixup
662 * st2.st_mode to match what we did ).
664 if ((st2
.st_mode
& 0700) != 0700) {
665 hc_chmod(&DstHost
, dpath
, st2
.st_mode
| 0700);
669 logstd("%s\n", dpath
? dpath
: spath
);
673 if ((int)sdevNo
>= 0 && st1
.st_dev
!= sdevNo
) {
679 if ((int)ddevNo
>= 0 && st2
.st_dev
!= ddevNo
) {
686 * scan .cpignore file for files/directories
695 if (UseCpFile
[0] == '/') {
696 fpath
= mprintf("%s", UseCpFile
);
698 fpath
= mprintf("%s/%s", spath
, UseCpFile
);
700 AddList(&list
, strrchr(fpath
, '/') + 1, 1);
701 if ((fi
= fopen(fpath
, "r")) != NULL
) {
702 while (fgets(buf
, sizeof(buf
), fi
) != NULL
) {
705 if (l
&& buf
[l
-1] == '\n')
707 if (buf
[0] && buf
[0] != '#')
708 AddList(&list
, buf
, 1);
716 * Automatically exclude MD5CacheFile that we create on the
717 * source from the copy to the destination.
719 * Automatically exclude a FSMIDCacheFile on the source that
720 * would otherwise overwrite the one we maintain on the target.
723 AddList(&list
, MD5CacheFile
, 1);
725 AddList(&list
, FSMIDCacheFile
, 1);
727 while (noLoop
== 0 && (den
= hc_readdir(&SrcHost
, dir
)) != NULL
) {
734 if (strcmp(den
->d_name
, ".") == 0 ||
735 strcmp(den
->d_name
, "..") == 0
740 * ignore if on .cpignore list
742 if (AddList(&list
, den
->d_name
, 0) == 1) {
745 nspath
= mprintf("%s/%s", spath
, den
->d_name
);
747 ndpath
= mprintf("%s/%s", dpath
, den
->d_name
);
759 hc_closedir(&SrcHost
, dir
);
762 * Remove files/directories from destination that do not appear
765 if (dpath
&& (dir
= hc_opendir(&DstHost
, dpath
)) != NULL
) {
766 while (noLoop
== 0 && (den
= hc_readdir(&DstHost
, dir
)) != NULL
) {
770 if (strcmp(den
->d_name
, ".") == 0 ||
771 strcmp(den
->d_name
, "..") == 0
776 * If object does not exist in source or .cpignore
777 * then recursively remove it.
779 if (AddList(&list
, den
->d_name
, 3) == 3) {
782 ndpath
= mprintf("%s/%s", dpath
, den
->d_name
);
783 RemoveRecur(ndpath
, ddevNo
);
787 hc_closedir(&DstHost
, dir
);
791 struct timeval tv
[2];
795 st1
.st_uid
!= st2
.st_uid
||
796 st1
.st_gid
!= st2
.st_gid
798 hc_chown(&DstHost
, dpath
, st1
.st_uid
, st1
.st_gid
);
800 if (st2Valid
== 0 || st1
.st_mode
!= st2
.st_mode
) {
801 hc_chmod(&DstHost
, dpath
, st1
.st_mode
);
803 #ifdef _ST_FLAGS_PRESENT_
804 if (st2Valid
== 0 || st1
.st_flags
!= st2
.st_flags
) {
805 hc_chflags(&DstHost
, dpath
, st1
.st_flags
);
810 st1
.st_mtime
!= st2
.st_mtime
812 bzero(tv
, sizeof(tv
));
813 tv
[0].tv_sec
= st1
.st_mtime
;
814 tv
[1].tv_sec
= st1
.st_mtime
;
815 hc_utimes(&DstHost
, dpath
, tv
);
819 } else if (dpath
== NULL
) {
821 * If dpath is NULL, we are just updating the MD5
824 if (UseMD5Opt
&& S_ISREG(st1
.st_mode
)) {
825 mres
= md5_check(spath
, NULL
);
827 if (VerboseOpt
> 1) {
829 logstd("%-32s md5-update\n", (dpath
) ? dpath
: spath
);
831 logstd("%-32s md5-ok\n", (dpath
) ? dpath
: spath
);
832 } else if (!QuietOpt
&& mres
< 0) {
833 logstd("%-32s md5-update\n", (dpath
) ? dpath
: spath
);
837 } else if (S_ISREG(st1
.st_mode
)) {
843 path
= mprintf("%s.tmp", dpath
);
846 * Handle check failure message.
850 logerr("%-32s md5-CHECK-FAILED\n", (dpath
) ? dpath
: spath
);
854 logerr("%-32s fsmid-CHECK-FAILED\n", (dpath
) ? dpath
: spath
);
857 * Not quite ready to do the copy yet. If UseHLPath is defined,
858 * see if we can hardlink instead.
860 * If we can hardlink, and the target exists, we have to remove it
861 * first or the hardlink will fail. This can occur in a number of
862 * situations but must typically when the '-f -H' combination is
865 if (UseHLPath
&& (hpath
= checkHLPath(&st1
, spath
, dpath
)) != NULL
) {
867 hc_remove(&DstHost
, dpath
);
868 if (hc_link(&DstHost
, hpath
, dpath
) == 0) {
870 logstd("%-32s hardlinked(-H)\n",
871 (dpath
? dpath
: spath
));
877 * Shucks, we may have hit a filesystem hard linking limit,
878 * we have to copy instead.
883 if ((fd1
= hc_open(&SrcHost
, spath
, O_RDONLY
, 0)) >= 0) {
884 if ((fd2
= hc_open(&DstHost
, path
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0) {
886 * There could be a .tmp file from a previously interrupted
887 * run, delete and retry. Fail if we still can't get at it.
889 #ifdef _ST_FLAGS_PRESENT_
890 hc_chflags(&DstHost
, path
, 0);
892 hc_remove(&DstHost
, path
);
893 fd2
= hc_open(&DstHost
, path
, O_WRONLY
|O_CREAT
|O_EXCL
|O_TRUNC
, 0600);
900 * Matt: What about holes?
903 while ((n
= hc_read(&SrcHost
, fd1
, IOBuf1
, sizeof(IOBuf1
))) > 0) {
905 if (hc_write(&DstHost
, fd2
, IOBuf1
, n
) != n
)
909 hc_close(&DstHost
, fd2
);
911 struct timeval tv
[2];
913 bzero(tv
, sizeof(tv
));
914 tv
[0].tv_sec
= st1
.st_mtime
;
915 tv
[1].tv_sec
= st1
.st_mtime
;
917 hc_utimes(&DstHost
, path
, tv
);
918 hc_chown(&DstHost
, path
, st1
.st_uid
, st1
.st_gid
);
919 hc_chmod(&DstHost
, path
, st1
.st_mode
);
920 if (xrename(path
, dpath
, st2
.st_flags
) != 0) {
921 logerr("%-32s rename-after-copy failed: %s\n",
922 (dpath
? dpath
: spath
), strerror(errno
)
927 logstd("%-32s copy-ok\n", (dpath
? dpath
: spath
));
928 #ifdef _ST_FLAGS_PRESENT_
930 hc_chflags(&DstHost
, dpath
, st1
.st_flags
);
933 CountReadBytes
+= size
;
934 CountWriteBytes
+= size
;
935 CountSourceBytes
+= size
;
939 logerr("%-32s %s failed: %s\n",
940 (dpath
? dpath
: spath
), op
, strerror(errno
)
942 hc_remove(&DstHost
, path
);
946 logerr("%-32s create (uid %d, euid %d) failed: %s\n",
947 (dpath
? dpath
: spath
), getuid(), geteuid(),
952 hc_close(&SrcHost
, fd1
);
954 logerr("%-32s copy: open failed: %s\n",
955 (dpath
? dpath
: spath
),
964 if (!r
&& hc_stat(&DstHost
, dpath
, &st2
) == 0)
965 hln
->dino
= st2
.st_ino
;
969 } else if (S_ISLNK(st1
.st_mode
)) {
976 snprintf(path
, sizeof(path
), "%s.tmp", dpath
);
977 n1
= hc_readlink(&SrcHost
, spath
, link1
, sizeof(link1
) - 1);
978 n2
= hc_readlink(&DstHost
, dpath
, link2
, sizeof(link2
) - 1);
980 if (ForceOpt
|| n1
!= n2
|| bcmp(link1
, link2
, n1
) != 0) {
981 hc_umask(&DstHost
, ~st1
.st_mode
);
982 hc_remove(&DstHost
, path
);
984 if (hc_symlink(&DstHost
, link1
, path
) < 0) {
985 logerr("%-32s symlink (%s->%s) failed: %s\n",
986 (dpath
? dpath
: spath
), link1
, path
,
991 hc_lchown(&DstHost
, path
, st1
.st_uid
, st1
.st_gid
);
993 * there is no lchmod() or lchflags(), we
994 * cannot chmod or chflags a softlink.
996 if (xrename(path
, dpath
, st2
.st_flags
) != 0) {
997 logerr("%-32s rename softlink (%s->%s) failed: %s\n",
998 (dpath
? dpath
: spath
),
999 path
, dpath
, strerror(errno
));
1000 } else if (VerboseOpt
) {
1001 logstd("%-32s softlink-ok\n", (dpath
? dpath
: spath
));
1003 hc_umask(&DstHost
, 000);
1004 CountWriteBytes
+= n1
;
1008 if (VerboseOpt
>= 3)
1009 logstd("%-32s nochange\n", (dpath
? dpath
: spath
));
1011 CountSourceBytes
+= n1
;
1012 CountReadBytes
+= n1
;
1013 if (n2
> 0) CountReadBytes
+= n2
;
1017 logerr("%-32s softlink-failed\n", (dpath
? dpath
: spath
));
1019 } else if ((S_ISCHR(st1
.st_mode
) || S_ISBLK(st1
.st_mode
)) && DeviceOpt
) {
1024 st1
.st_mode
!= st2
.st_mode
||
1025 st1
.st_rdev
!= st2
.st_rdev
||
1026 st1
.st_uid
!= st2
.st_uid
||
1027 st1
.st_gid
!= st2
.st_gid
1029 snprintf(path
, sizeof(path
), "%s.tmp", dpath
);
1031 hc_remove(&DstHost
, path
);
1032 if (mknod(path
, st1
.st_mode
, st1
.st_rdev
) == 0) {
1033 hc_chmod(&DstHost
, path
, st1
.st_mode
);
1034 hc_chown(&DstHost
, path
, st1
.st_uid
, st1
.st_gid
);
1035 hc_remove(&DstHost
, dpath
);
1036 if (xrename(path
, dpath
, st2
.st_flags
) != 0) {
1037 logerr("%-32s dev-rename-after-create failed: %s\n",
1038 (dpath
? dpath
: spath
),
1041 } else if (VerboseOpt
) {
1042 logstd("%-32s dev-ok\n", (dpath
? dpath
: spath
));
1047 logerr("%-32s dev failed: %s\n",
1048 (dpath
? dpath
: spath
), strerror(errno
)
1052 if (VerboseOpt
>= 3)
1053 logstd("%-32s nochange\n", (dpath
? dpath
: spath
));
1066 RemoveRecur(const char *dpath
, dev_t devNo
)
1070 if (hc_lstat(&DstHost
, dpath
, &st
) == 0) {
1073 if (st
.st_dev
== devNo
) {
1074 if (S_ISDIR(st
.st_mode
)) {
1077 if ((dir
= hc_opendir(&DstHost
, dpath
)) != NULL
) {
1079 while ((den
= hc_readdir(&DstHost
, dir
)) != NULL
) {
1082 if (strcmp(den
->d_name
, ".") == 0)
1084 if (strcmp(den
->d_name
, "..") == 0)
1086 ndpath
= mprintf("%s/%s", dpath
, den
->d_name
);
1087 RemoveRecur(ndpath
, devNo
);
1090 hc_closedir(&DstHost
, dir
);
1092 if (AskConfirmation
&& NoRemoveOpt
== 0) {
1094 if (hc_rmdir(&DstHost
, dpath
) < 0) {
1095 logerr("%-32s rmdir failed: %s\n",
1096 dpath
, strerror(errno
)
1099 CountRemovedItems
++;
1104 logstd("%-32s not-removed\n", dpath
);
1105 } else if (hc_rmdir(&DstHost
, dpath
) == 0) {
1107 logstd("%-32s rmdir-ok\n", dpath
);
1108 CountRemovedItems
++;
1110 logerr("%-32s rmdir failed: %s\n",
1111 dpath
, strerror(errno
)
1116 if (AskConfirmation
&& NoRemoveOpt
== 0) {
1118 if (hc_remove(&DstHost
, dpath
) < 0) {
1119 logerr("%-32s remove failed: %s\n",
1120 dpath
, strerror(errno
)
1123 CountRemovedItems
++;
1128 logstd("%-32s not-removed\n", dpath
);
1129 } else if (hc_remove(&DstHost
, dpath
) == 0) {
1131 logstd("%-32s remove-ok\n", dpath
);
1132 CountRemovedItems
++;
1134 logerr("%-32s remove failed: %s\n",
1135 dpath
, strerror(errno
)
1145 InitList(List
*list
)
1147 bzero(list
, sizeof(List
));
1148 list
->li_Node
.no_Next
= &list
->li_Node
;
1152 ResetList(List
*list
)
1156 while ((node
= list
->li_Node
.no_Next
) != &list
->li_Node
) {
1157 list
->li_Node
.no_Next
= node
->no_Next
;
1164 AddList(List
*list
, const char *name
, int n
)
1172 * Scan against wildcards. Only a node value of 1 can be a wildcard
1173 * ( usually scanned from .cpignore )
1176 for (node
= list
->li_Hash
[0]; node
; node
= node
->no_HNext
) {
1177 if (strcmp(name
, node
->no_Name
) == 0 ||
1178 (n
!= 1 && node
->no_Value
== 1 && WildCmp(node
->no_Name
, name
) == 0)
1180 return(node
->no_Value
);
1185 * Look for exact match
1188 for (node
= list
->li_Hash
[hv
]; node
; node
= node
->no_HNext
) {
1189 if (strcmp(name
, node
->no_Name
) == 0) {
1190 return(node
->no_Value
);
1193 node
= malloc(sizeof(Node
) + strlen(name
) + 1);
1195 fprintf(stderr
, "out of memory\n");
1199 node
->no_Next
= list
->li_Node
.no_Next
;
1200 list
->li_Node
.no_Next
= node
;
1202 node
->no_HNext
= list
->li_Hash
[hv
];
1203 list
->li_Hash
[hv
] = node
;
1205 strcpy(node
->no_Name
, name
);
1212 shash(const char *s
)
1219 if (*s
== '*' || *s
== '?' ||
1220 *s
== '{' || *s
== '}' ||
1221 *s
== '[' || *s
== ']' ||
1226 hv
= (hv
<< 5) ^ *s
^ (hv
>> 23);
1229 return(((hv
>> 16) ^ hv
) & HMASK
);
1233 * WildCmp() - compare wild string to sane string
1235 * Return 0 on success, -1 on failure.
1239 WildCmp(const char *w
, const char *s
)
1242 * skip fixed portion
1248 if (w
[1] == 0) /* optimize wild* case */
1254 for (i
= 0; i
<= l
; ++i
) {
1255 if (WildCmp(w
+ 1, s
+ i
) == 0)
1269 if (*w
== 0) /* terminator */
1281 YesNo(const char *path
)
1285 fprintf(stderr
, "remove %s (Yes/No) [No]? ", path
);
1288 first
= ch
= getchar();
1289 while (ch
!= '\n' && ch
!= EOF
)
1291 return ((first
== 'y' || first
== 'Y'));
1295 * xrename() - rename with override
1297 * If the rename fails, attempt to override st_flags on the
1298 * destination and rename again. If that fails too, try to
1299 * set the flags back the way they were and give up.
1303 xrename(const char *src
, const char *dst
, u_long flags
)
1309 if ((r
= hc_rename(&DstHost
, src
, dst
)) < 0) {
1310 #ifdef _ST_FLAGS_PRESENT_
1311 hc_chflags(&DstHost
, dst
, 0);
1312 if ((r
= hc_rename(&DstHost
, src
, dst
)) < 0)
1313 hc_chflags(&DstHost
, dst
, flags
);
1320 xlink(const char *src
, const char *dst
, u_long flags
)
1323 #ifdef _ST_FLAGS_PRESENT_
1329 if ((r
= hc_link(&DstHost
, src
, dst
)) < 0) {
1330 #ifdef _ST_FLAGS_PRESENT_
1331 hc_chflags(&DstHost
, src
, 0);
1332 r
= hc_link(&DstHost
, src
, dst
);
1334 hc_chflags(&DstHost
, src
, flags
);