Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 29 additions & 14 deletions cpdup.1
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
.Nm
.Op Fl C
.Op Fl v Ns Op Cm v Ns Op Cm v
.Op Fl c
.Op Fl d
.Op Fl n
.Op Fl u
Expand Down Expand Up @@ -73,6 +74,27 @@ modifications made to the destination.
.Fl vvv
will cause all files and directories to be reported whether or not
modifications are made.
.It Fl c Ar digest
Generate and maintain a checksum file using the specified message
.Ar digest
called
.Pa \&.DIGEST.CHECKSUMS
in each directory on the source where
.Pa DIGEST
is replaced by the upperscale name of the message digest used.
An alternate file name may be specified with the
.Fl M Ar file
option. Supported are all localy available OpenSSL
.Xr EVP 7
message digests, e.g. md5, rmd160, sha1, sha256 or sha512.
A checksum check is done on each file of the destination when the destination
appears to be the same as the source. If the check fails,
the source is recopied to the destination. When you specify a destination
directory, the checksum file is only updated as needed and may not be updated
even if modifications are made to a source file. If you do not specify a
destination directory the
.Nm
command forcefully regenerates the checksum for every file in the source.
.It Fl d
Print directories as they are being traversed.
Useful to watch the progress;
Expand Down Expand Up @@ -120,21 +142,14 @@ Quiet operation.
.It Fl o
Do not remove any files, just overwrite/add.
.It Fl m
Generate and maintain a MD5 checkfile called
.Pa \&.MD5.CHECKSUMS
in each directory on the source
and do an MD5 check on each file of the destination when the destination
appears to be the same as the source. If the check fails,
the source is recopied to the destination. When you specify a destination
directory, the MD5 checkfile is only updated as needed and may not be updated
even if modifications are made to a source file. If you do not specify a
destination directory the
.Nm
command forcefully regenerates the MD5 checkfile for every file in the source.
.It Fl M Ar file
Works the same as
.Fl m
but allows you to specify the name of the MD5 checkfile.
.Fl c Ar md5
for compatibility purposes
.It Fl M Ar file
allows you to specify the name of the checksum file generated by options
.Fl c
or
.Fl m.
.It Fl H Ar path
.Nm
will create a hardlink from a file found under
Expand Down
81 changes: 65 additions & 16 deletions src/cpdup.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,16 @@
* standard wildcarded ( ? / * style, NOT regex) exclusions.
* - tries to play permissions and flags smart in regards to overwriting
* schg files and doing related stuff.
* - Can do MD5 consistancy checks
* - Can do checksum consistancy checks with any supported OpenSSL EVP
* message digest
* - Is able to do incremental mirroring/backups via hardlinks from
* the 'previous' version (supplied with -H path).
*/

#include "cpdup.h"
#include "hclink.h"
#include "hcproto.h"
#include <ctype.h>

#define HSIZE 8192
#define HMASK (HSIZE-1)
Expand Down Expand Up @@ -188,23 +190,43 @@ int64_t CountLinkedItems;
static struct HostConf SrcHost;
static struct HostConf DstHost;

#ifndef NOMD5
const EVP_MD *CsumAlgo;
#endif

int
main(int ac, char **av)
{
int i;
#ifndef NOMD5
int len;
#endif
int opt;
char *src = NULL;
char *dst = NULL;
char *ptr;
struct timeval start;
struct copy_info info;

#ifndef NOMD5
char *MD5AlgoStr;
char *MD5CacheFileStr;
const char *CsumAlgoStrArg = NULL;
const char *MD5CacheFileArg = NULL;
#endif

signal(SIGPIPE, SIG_IGN);

gettimeofday(&start, NULL);
opterr = 0;
while ((opt = getopt(ac, av, ":CdF:fH:hIi:j:lM:mnoqRSs:uVvX:x")) != -1) {
while ((opt = getopt(ac, av, ":c:CdF:fH:hIi:j:lM:mnoqRSs:uVvX:x")) != -1) {
switch (opt) {
case 'c':
UseMD5Opt = 1;
#ifndef NOMD5
CsumAlgoStrArg = optarg;
#endif
break;
case 'C':
CompressOpt = 1;
break;
Expand Down Expand Up @@ -241,11 +263,17 @@ main(int ac, char **av)
break;
case 'M':
UseMD5Opt = 1;
MD5CacheFile = optarg;
if (strnlen(optarg, PATH_MAX) == PATH_MAX)
fatal("Cache file string too long");
#ifndef NOMD5
MD5CacheFileArg = optarg;
#endif
break;
case 'm':
UseMD5Opt = 1;
MD5CacheFile = ".MD5.CHECKSUMS";
#ifndef NOMD5
CsumAlgoStrArg = "MD5";
#endif
break;
case 'n':
NotForRealOpt = 1;
Expand Down Expand Up @@ -303,6 +331,27 @@ main(int ac, char **av)
if (ac > 2)
fatal("too many arguments");

#ifndef NOMD5
if (UseMD5Opt) {
if (CsumAlgoStrArg == NULL)
CsumAlgoStrArg = "MD5";
CsumAlgo = EVP_get_digestbyname(CsumAlgoStrArg);
if (CsumAlgo == NULL)
fatal("Unknown digest algorithm: %s", CsumAlgoStrArg);
len = strlen(CsumAlgoStrArg);
CsumAlgoStr = malloc(len + 1);
for (i = 0; i < len; i++)
CsumAlgoStr[i] = toupper(CsumAlgoStrArg[i]);
CsumAlgoStr[i] = '\0';
if (MD5CacheFileArg == NULL) {
if (asprintf(&MD5CacheFileStr, ".%s.CHECKSUMS", CsumAlgoStr) < 0)
fatal("Memory allocation error\n");
} else
MD5CacheFileStr = strdup(MD5CacheFileArg);
MD5CacheFile = MD5CacheFileStr;
}
#endif

/*
* If we are told to go into slave mode, run the HC protocol
*/
Expand All @@ -320,7 +369,7 @@ main(int ac, char **av)
SrcHost.host = src;
src = ptr;
if (UseMD5Opt)
fatal("The MD5 options are not currently supported for remote sources");
fatal("The checksum options are not currently supported for remote sources");
if (hc_connect(&SrcHost, ReadOnlyOpt) < 0)
exit(1);
} else {
Expand All @@ -339,8 +388,8 @@ main(int ac, char **av)
}

/*
* dst may be NULL only if -m option is specified,
* which forces an update of the MD5 checksums
* dst may be NULL only if -c checksum or -m option is specified,
* which forces an update of the checksums
*/
if (dst == NULL && UseMD5Opt == 0) {
fatal(NULL);
Expand Down Expand Up @@ -830,7 +879,7 @@ DoCopy(copy_info_t info, struct stat *stat1, int depth)
OwnerMatch(stat1, &st2)
#ifndef NOMD5
&& (UseMD5Opt == 0 || !S_ISREG(stat1->st_mode) ||
(mres = md5_check(spath, dpath)) == 0)
(mres = md5_check(CsumAlgo, spath, dpath)) == 0)
#endif
&& (ValidateOpt == 0 || !S_ISREG(stat1->st_mode) ||
validate_check(spath, dpath) == 0)
Expand Down Expand Up @@ -858,7 +907,7 @@ DoCopy(copy_info_t info, struct stat *stat1, int depth)
if (VerboseOpt >= 3) {
#ifndef NOMD5
if (UseMD5Opt) {
logstd("%-32s md5-nochange",
logstd("%-32s checksum-nochange",
(dpath ? dpath : spath));
} else
#endif
Expand Down Expand Up @@ -1063,22 +1112,22 @@ DoCopy(copy_info_t info, struct stat *stat1, int depth)
}
} else if (dpath == NULL) {
/*
* If dpath is NULL, we are just updating the MD5
* If dpath is NULL, we are just updating the checksum
*/
#ifndef NOMD5
if (UseMD5Opt && S_ISREG(stat1->st_mode)) {
mres = md5_update(spath);
mres = md5_update(CsumAlgo, spath);

if (mres < 0) {
logerr("%-32s md5-CHECK-FAILED\n", spath);
logerr("%-32s checksum-CHECK-FAILED\n", spath);
} else {
if (VerboseOpt > 1) {
if (mres > 0)
logstd("%-32s md5-update\n", spath);
logstd("%-32s checksum-update\n", spath);
else
logstd("%-32s md5-ok\n", spath);
logstd("%-32s checksum-ok\n", spath);
} else if (!QuietOpt && mres > 0) {
logstd("%-32s md5-update\n", spath);
logstd("%-32s checksum-update\n", spath);
}
}
}
Expand All @@ -1099,7 +1148,7 @@ DoCopy(copy_info_t info, struct stat *stat1, int depth)
* Handle check failure message.
*/
if (mres < 0)
logerr("%-32s md5-CHECK-FAILED\n", (dpath) ? dpath : spath);
logerr("%-32s checksum-CHECK-FAILED\n", (dpath) ? dpath : spath);
#endif

/*
Expand Down
8 changes: 6 additions & 2 deletions src/cpdup.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
#include <fnmatch.h>
#include <assert.h>

#ifndef NOMD5
#include <openssl/evp.h>
#endif

#ifdef __linux

/*
Expand Down Expand Up @@ -100,8 +104,8 @@ int32_t hc_bswap32(int32_t var);
int64_t hc_bswap64(int64_t var);

#ifndef NOMD5
int md5_update(const char *spath);
int md5_check(const char *spath, const char *dpath);
int md5_update(const EVP_MD *algo, const char *spath);
int md5_check(const EVP_MD *algo, const char *spath, const char *dpath);
void md5_flush(void);
#endif

Expand Down
Loading