From 375a4556c7a1ffb9a4e7117f33fc42ed2bc4c026 Mon Sep 17 00:00:00 2001 From: David Dykstra Date: Mon, 26 Oct 1998 21:42:38 +0000 Subject: [PATCH] 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. --- generator.c | 26 ++++++++++++++++++++++---- options.c | 19 ++++++++++++++++++- receiver.c | 21 +++++++++++++++++---- rsync.yo | 15 +++++++++++++-- util.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 11 deletions(-) diff --git a/generator.c b/generator.c index ffde4444..99052220 100644 --- a/generator.c +++ b/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)); diff --git a/options.c b/options.c index 23b3c8cd..a79443fc 100644 --- a/options.c +++ b/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; } diff --git a/receiver.c b/receiver.c index c565a6f6..204933ff 100644 --- a/receiver.c +++ b/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; } diff --git a/rsync.yo b/rsync.yo index 8da75b99..cedf5781 100644 --- a/rsync.yo +++ b/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 diff --git a/util.c b/util.c index 6e0fbaba..3c23cb7a 100644 --- a/util.c +++ b/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 "/.." (recursively) + * Return a malloc'ed copy. + * Contributed by Dave Dykstra + */ + +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); +} + -- 2.34.1