Add --compare-dest option which enables specifying an additional destination
authorDavid Dykstra <dwd@samba.org>
Mon, 26 Oct 1998 21:42:38 +0000 (21:42 +0000)
committerDavid Dykstra <dwd@samba.org>
Mon, 26 Oct 1998 21:42:38 +0000 (21:42 +0000)
for comparisons when syncing.  Useful for syncing into a scratch area and
doing a flash-cutover when it is completed.

generator.c
options.c
receiver.c
rsync.yo
util.c

index ffde444..9905222 100644 (file)
@@ -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));
 
index 23b3c8c..a79443f 100644 (file)
--- 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;
 }
 
index c565a6f..204933f 100644 (file)
@@ -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;
                }
index 8da75b9..cedf578 100644 (file)
--- 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 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 6e0fbab..3c23cb7 100644 (file)
--- 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 "<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);
+}
+