mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-04-29 02:25:45 -04:00
Add --compare-dest option which enables specifying an additional destination
for comparisons when syncing. Useful for syncing into a scratch area and doing a flash-cutover when it is completed.
This commit is contained in:
26
generator.c
26
generator.c
@@ -162,6 +162,9 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
struct sum_struct *s;
|
||||
int statret;
|
||||
struct file_struct *file = flist->files[i];
|
||||
char *fnamecmp;
|
||||
char fnamecmpbuf[MAXPATHLEN];
|
||||
extern char *compare_dest;
|
||||
|
||||
if (verbose > 2)
|
||||
rprintf(FINFO,"recv_generator(%s,%d)\n",fname,i);
|
||||
@@ -262,6 +265,21 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
return;
|
||||
}
|
||||
|
||||
fnamecmp = fname;
|
||||
|
||||
if ((statret == -1) && (compare_dest != NULL)) {
|
||||
/* try the file at compare_dest instead */
|
||||
int saveerrno = errno;
|
||||
slprintf(fnamecmpbuf,MAXPATHLEN-1,"%s/%s",compare_dest,fname);
|
||||
statret = link_stat(fnamecmpbuf,&st);
|
||||
if (!S_ISREG(st.st_mode))
|
||||
statret = -1;
|
||||
if (statret == -1)
|
||||
errno = saveerrno;
|
||||
else
|
||||
fnamecmp = fnamecmpbuf;
|
||||
}
|
||||
|
||||
if (statret == -1) {
|
||||
if (errno == ENOENT) {
|
||||
write_int(f_out,i);
|
||||
@@ -284,7 +302,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_only && st.st_mtime > file->modtime) {
|
||||
if (update_only && st.st_mtime > file->modtime && fnamecmp == fname) {
|
||||
if (verbose > 1)
|
||||
rprintf(FINFO,"%s is newer\n",fname);
|
||||
return;
|
||||
@@ -307,10 +325,10 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
}
|
||||
|
||||
/* open the file */
|
||||
fd = open(fname,O_RDONLY);
|
||||
fd = open(fnamecmp,O_RDONLY);
|
||||
|
||||
if (fd == -1) {
|
||||
rprintf(FERROR,"failed to open %s : %s\n",fname,strerror(errno));
|
||||
rprintf(FERROR,"failed to open %s : %s\n",fnamecmp,strerror(errno));
|
||||
rprintf(FERROR,"skipping %s\n",fname);
|
||||
return;
|
||||
}
|
||||
@@ -322,7 +340,7 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
}
|
||||
|
||||
if (verbose > 3)
|
||||
rprintf(FINFO,"gen mapped %s of size %d\n",fname,(int)st.st_size);
|
||||
rprintf(FINFO,"gen mapped %s of size %d\n",fnamecmp,(int)st.st_size);
|
||||
|
||||
s = generate_sums(buf,st.st_size,adapt_block_size(file, block_size));
|
||||
|
||||
|
||||
19
options.c
19
options.c
@@ -64,6 +64,7 @@ int block_size=BLOCK_SIZE;
|
||||
|
||||
char *backup_suffix = BACKUP_SUFFIX;
|
||||
char *tmpdir = NULL;
|
||||
char *compare_dest = NULL;
|
||||
char *config_file = RSYNCD_CONF;
|
||||
char *shell_cmd = NULL;
|
||||
|
||||
@@ -118,6 +119,7 @@ void usage(int F)
|
||||
rprintf(F," --timeout=TIME set IO timeout in seconds\n");
|
||||
rprintf(F," -I, --ignore-times don't exclude files that match length and time\n");
|
||||
rprintf(F," -T --temp-dir=DIR create temporary files in directory DIR\n");
|
||||
rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n");
|
||||
rprintf(F," -z, --compress compress file data\n");
|
||||
rprintf(F," --exclude=PATTERN exclude file FILE\n");
|
||||
rprintf(F," --exclude-from=FILE exclude patterns listed in FILE\n");
|
||||
@@ -144,7 +146,7 @@ enum {OPT_VERSION,OPT_SUFFIX,OPT_SENDER,OPT_SERVER,OPT_EXCLUDE,
|
||||
OPT_EXCLUDE_FROM,OPT_DELETE,OPT_NUMERIC_IDS,OPT_RSYNC_PATH,
|
||||
OPT_FORCE,OPT_TIMEOUT,OPT_DAEMON,OPT_CONFIG,OPT_PORT,
|
||||
OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_STATS, OPT_PARTIAL, OPT_PROGRESS,
|
||||
OPT_SAFE_LINKS};
|
||||
OPT_SAFE_LINKS, OPT_COMPARE_DEST};
|
||||
|
||||
static char *short_options = "oblLWHpguDCtcahvrRIxnSe:B:T:z";
|
||||
|
||||
@@ -188,6 +190,7 @@ static struct option long_options[] = {
|
||||
{"block-size", 1, 0, 'B'},
|
||||
{"timeout", 1, 0, OPT_TIMEOUT},
|
||||
{"temp-dir", 1, 0, 'T'},
|
||||
{"compare-dest", 1, 0, OPT_COMPARE_DEST},
|
||||
{"compress", 0, 0, 'z'},
|
||||
{"daemon", 0, 0, OPT_DAEMON},
|
||||
{"stats", 0, 0, OPT_STATS},
|
||||
@@ -384,6 +387,10 @@ int parse_arguments(int argc, char *argv[])
|
||||
tmpdir = optarg;
|
||||
break;
|
||||
|
||||
case OPT_COMPARE_DEST:
|
||||
compare_dest = optarg;
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
do_compression = 1;
|
||||
break;
|
||||
@@ -516,6 +523,16 @@ void server_options(char **args,int *argc)
|
||||
args[ac++] = tmpdir;
|
||||
}
|
||||
|
||||
if (compare_dest && am_sender) {
|
||||
/* the server only needs this option if it is not the sender,
|
||||
* and it may be an older version that doesn't know this
|
||||
* option, so don't send it if client is the sender.
|
||||
*/
|
||||
args[ac++] = "--compare-dest";
|
||||
args[ac++] = compare_dest;
|
||||
}
|
||||
|
||||
|
||||
*argc = ac;
|
||||
}
|
||||
|
||||
|
||||
21
receiver.c
21
receiver.c
@@ -32,6 +32,7 @@ extern int preserve_hard_links;
|
||||
extern int cvs_exclude;
|
||||
extern int io_error;
|
||||
extern char *tmpdir;
|
||||
extern char *compare_dest;
|
||||
|
||||
|
||||
static struct delete_list {
|
||||
@@ -284,6 +285,8 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
STRUCT_STAT st;
|
||||
char *fname;
|
||||
char fnametmp[MAXPATHLEN];
|
||||
char *fnamecmp;
|
||||
char fnamecmpbuf[MAXPATHLEN];
|
||||
struct map_struct *buf;
|
||||
int i;
|
||||
struct file_struct *file;
|
||||
@@ -338,18 +341,28 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
if (verbose > 2)
|
||||
rprintf(FINFO,"recv_files(%s)\n",fname);
|
||||
|
||||
fnamecmp = fname;
|
||||
|
||||
/* open the file */
|
||||
fd1 = open(fname,O_RDONLY);
|
||||
fd1 = open(fnamecmp,O_RDONLY);
|
||||
|
||||
if ((fd1 == -1) && (compare_dest != NULL)) {
|
||||
/* try the file at compare_dest instead */
|
||||
slprintf(fnamecmpbuf,MAXPATHLEN-1,"%s/%s",
|
||||
compare_dest,fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
fd1 = open(fnamecmp,O_RDONLY);
|
||||
}
|
||||
|
||||
if (fd1 != -1 && do_fstat(fd1,&st) != 0) {
|
||||
rprintf(FERROR,"fstat %s : %s\n",fname,strerror(errno));
|
||||
rprintf(FERROR,"fstat %s : %s\n",fnamecmp,strerror(errno));
|
||||
receive_data(f_in,NULL,-1,NULL,file->length);
|
||||
close(fd1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fd1 != -1 && !S_ISREG(st.st_mode)) {
|
||||
rprintf(FERROR,"%s : not a regular file (recv_files)\n",fname);
|
||||
rprintf(FERROR,"%s : not a regular file (recv_files)\n",fnamecmp);
|
||||
receive_data(f_in,NULL,-1,NULL,file->length);
|
||||
close(fd1);
|
||||
continue;
|
||||
@@ -358,7 +371,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
if (fd1 != -1 && st.st_size > 0) {
|
||||
buf = map_file(fd1,st.st_size);
|
||||
if (verbose > 2)
|
||||
rprintf(FINFO,"recv mapped %s of size %d\n",fname,(int)st.st_size);
|
||||
rprintf(FINFO,"recv mapped %s of size %d\n",fnamecmp,(int)st.st_size);
|
||||
} else {
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
15
rsync.yo
15
rsync.yo
@@ -231,6 +231,7 @@ Options
|
||||
--timeout=TIME set IO timeout in seconds
|
||||
-I, --ignore-times don't exclude files that match length and time
|
||||
-T --temp-dir=DIR create temporary files in directory DIR
|
||||
--compare-dest=DIR also compare destination files relative to DIR
|
||||
-z, --compress compress file data
|
||||
--exclude=PATTERN exclude file FILE
|
||||
--exclude-from=PATTERN exclude files listed in FILE
|
||||
@@ -477,11 +478,21 @@ checksum length by default, using a 16 byte file checksum to determine
|
||||
if a 2nd pass is required with a longer block checksum. Only use this
|
||||
option if you have read the source code and know what you are doing.
|
||||
|
||||
dit(bf(-T, --temp-dir DIR)) This options instructs rsync to use DIR as a
|
||||
scratch directory when creating a temporary copies of the files
|
||||
dit(bf(-T, --temp-dir DIR)) This option instructs rsync to use DIR as a
|
||||
scratch directory when creating temporary copies of the files
|
||||
transferred on the receiving side. The default behavior is to create
|
||||
the temporary files in the receiving directory.
|
||||
|
||||
dit(bf(--compare-dest DIR)) This option instructs rsync to use DIR as an
|
||||
additional directory to compare destination files against when doing
|
||||
transfers. This is useful for doing transfers to a new destination while
|
||||
leaving existing files intact, and then doing a flash-cutover when all
|
||||
files have been successfully transfered (for example by moving directories
|
||||
around and removing the old directory). This option increases the
|
||||
usefulness of --partial because partially transferred files will remain in
|
||||
the new temporary destination until they have a chance to be completed.
|
||||
If DIR is a relative path, it is relative to the destination directory.
|
||||
|
||||
dit(bf(-z, --compress)) With this option, rsync compresses any data from
|
||||
the source file(s) which it sends to the destination machine. This
|
||||
option is useful on slow links. The compression method used is the
|
||||
|
||||
47
util.c
47
util.c
@@ -752,3 +752,50 @@ int unsafe_symlink(char *dest, char *src)
|
||||
free(dest);
|
||||
return (depth < 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make path appear as if a chroot had occurred:
|
||||
* 1. remove leading "/" (or replace with "." if at end)
|
||||
* 2. remove leading ".." components
|
||||
* 3. delete any other "<dir>/.." (recursively)
|
||||
* Return a malloc'ed copy.
|
||||
* Contributed by Dave Dykstra <dwd@bell-labs.com>
|
||||
*/
|
||||
|
||||
char *sanitize_path(char *p)
|
||||
{
|
||||
char *copy, *copyp;
|
||||
|
||||
copy = (char *) malloc(strlen(p)+1);
|
||||
copyp = copy;
|
||||
while (*p != '\0') {
|
||||
if ((*p == '/') && (copyp == copy)) {
|
||||
/* remove leading slash */
|
||||
p++;
|
||||
}
|
||||
else if ((*p == '.') && (*(p+1) == '.') &&
|
||||
((*(p+2) == '/') || (*(p+2) == '\0'))) {
|
||||
/* remove .. followed by slash or end */
|
||||
p += 2;
|
||||
if (copyp != copy) {
|
||||
/* backup the copy one level */
|
||||
while ((--copyp != copy) && (*copyp == '/'))
|
||||
/* skip trailing slashes */
|
||||
;
|
||||
while ((copyp != copy) && (*copyp != '/'))
|
||||
/* skip back through slash */
|
||||
copyp--;
|
||||
}
|
||||
} else {
|
||||
/* copy one component */
|
||||
while (1) {
|
||||
*copyp++ = *p++;
|
||||
if ((*p == '\0') || (*(p-1) == '/'))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*copyp = '\0';
|
||||
return(copy);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user