The patches for 3.0.1pre3.
authorWayne Davison <wayned@samba.org>
Mon, 31 Mar 2008 06:48:35 +0000 (23:48 -0700)
committerWayne Davison <wayned@samba.org>
Mon, 31 Mar 2008 06:48:35 +0000 (23:48 -0700)
22 files changed:
atimes.diff
checksum-reading.diff
checksum-updating.diff
checksum-xattr.diff
copy-devices.diff
crtimes.diff
cvs-entries.diff
db.diff [new file with mode: 0644]
detect-renamed-lax.diff
detect-renamed.diff
downdate.diff
fileflags.diff
ignore-case.diff
link-by-hash.diff
openssl-support.diff
preallocate.diff
remote-option.diff
slow-down.diff
slp.diff
source-filter_dest-filter.diff
stdout.diff
usermap.diff

index b25b217..462a40b 100644 (file)
@@ -44,7 +44,7 @@ diff --git a/flist.c b/flist.c
  extern int relative_paths;
  extern int implied_dirs;
  extern int file_extra_cnt;
-@@ -379,7 +380,7 @@ int push_pathname(const char *dir, int len)
+@@ -384,7 +385,7 @@ int change_pathname(struct file_struct *file, const char *dir, int dirlen)
  
  static void send_file_entry(int f, const char *fname, struct file_struct *file, int ndx, int first_ndx)
  {
@@ -53,7 +53,7 @@ diff --git a/flist.c b/flist.c
        static mode_t mode;
  #ifdef SUPPORT_HARD_LINKS
        static int64 dev;
-@@ -457,6 +458,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+@@ -458,6 +459,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                xflags |= XMIT_SAME_TIME;
        else
                modtime = file->modtime;
@@ -111,7 +111,7 @@ diff --git a/flist.c b/flist.c
        if (unsort_ndx)
                F_NDX(file) = flist->used + flist->ndx_start;
  
-@@ -1215,6 +1237,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1226,6 +1248,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                F_OWNER(file) = st.st_uid;
        if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
                F_GROUP(file) = st.st_gid;
@@ -177,7 +177,7 @@ diff --git a/generator.c b/generator.c
                        f_name(f, NULL));
        }
  }
-@@ -2017,7 +2026,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
+@@ -2016,7 +2025,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
                        STRUCT_STAT st;
                        if (link_stat(fname, &st, 0) == 0
                         && cmp_time(st.st_mtime, file->modtime) != 0)
@@ -352,7 +352,7 @@ diff --git a/rsync.h b/rsync.h
  
  /* These flags are used in the live flist data. */
  
-@@ -151,6 +152,7 @@
+@@ -152,6 +153,7 @@
  
  #define ATTRS_REPORT          (1<<0)
  #define ATTRS_SKIP_MTIME      (1<<1)
@@ -360,7 +360,7 @@ diff --git a/rsync.h b/rsync.h
  
  #define FULL_FLUSH    1
  #define NORMAL_FLUSH  0
-@@ -622,12 +624,14 @@ extern int file_extra_cnt;
+@@ -626,12 +628,14 @@ extern int file_extra_cnt;
  extern int inc_recurse;
  extern int uid_ndx;
  extern int gid_ndx;
index 784a91b..159c7ad 100644 (file)
@@ -103,7 +103,7 @@ diff --git a/flist.c b/flist.c
  static void output_flist(struct file_list *flist);
  
  void init_flist(void)
-@@ -350,6 +363,238 @@ static void flist_done_allocating(struct file_list *flist)
+@@ -338,6 +351,238 @@ static void flist_done_allocating(struct file_list *flist)
                flist->pool_boundary = ptr;
  }
  
@@ -339,9 +339,9 @@ diff --git a/flist.c b/flist.c
 +      file_checksum(fname, stp->st_size, sum_buf);
 +}
 +
- int push_pathname(const char *dir, int len)
- {
-       if (dir == pathname)
+ /* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
+  * F_PATHNAME(), or (2) "NULL, dir, dirlen" to chdir() to the supplied dir,
+  * with dir == NULL taken to be the starting directory, and dirlen < 0
 @@ -1010,7 +1255,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                              STRUCT_STAT *stp, int flags, int filter_level)
  {
@@ -351,7 +351,7 @@ diff --git a/flist.c b/flist.c
        struct file_struct *file;
        char thisname[MAXPATHLEN];
        char linkname[MAXPATHLEN];
-@@ -1149,9 +1394,16 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1160,9 +1405,16 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                        memcpy(lastdir, thisname, len);
                        lastdir[len] = '\0';
                        lastdir_len = len;
@@ -369,7 +369,7 @@ diff --git a/flist.c b/flist.c
        basename_len = strlen(basename) + 1; /* count the '\0' */
  
  #ifdef SUPPORT_LINKS
-@@ -1224,14 +1476,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1235,14 +1487,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                memcpy(bp + basename_len, linkname, linkname_len);
  #endif
  
@@ -391,7 +391,7 @@ diff --git a/flist.c b/flist.c
        /* This code is only used by the receiver when it is building
         * a list of files for a delete pass. */
        if (keep_dirlinks && linkname_len && flist) {
-@@ -2147,7 +2403,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
+@@ -2155,7 +2411,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                         * file-list to check if this is a 1-file xfer. */
                        send_extra_file_list(f, 1);
                }
@@ -401,7 +401,7 @@ diff --git a/flist.c b/flist.c
  
        return flist;
  }
-@@ -2249,7 +2506,7 @@ struct file_list *recv_file_list(int f)
+@@ -2257,7 +2514,7 @@ struct file_list *recv_file_list(int f)
        else if (f >= 0)
                recv_id_list(f, flist);
  
@@ -410,7 +410,7 @@ diff --git a/flist.c b/flist.c
  
        if (protocol_version < 30) {
                /* Recv the io_error flag */
-@@ -2447,7 +2704,7 @@ void flist_free(struct file_list *flist)
+@@ -2455,7 +2712,7 @@ void flist_free(struct file_list *flist)
  
  /* This routine ensures we don't have any duplicate names in our file list.
   * duplicate names can cause corruption because of the pipelining. */
@@ -419,7 +419,7 @@ diff --git a/flist.c b/flist.c
  {
        char fbuf[MAXPATHLEN];
        int i, prev_i;
-@@ -2498,7 +2755,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+@@ -2506,7 +2763,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
                        /* If one is a dir and the other is not, we want to
                         * keep the dir because it might have contents in the
                         * list.  Otherwise keep the first one. */
@@ -428,7 +428,7 @@ diff --git a/flist.c b/flist.c
                                struct file_struct *fp = flist->sorted[j];
                                if (!S_ISDIR(fp->mode))
                                        keep = i, drop = j;
-@@ -2514,8 +2771,8 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+@@ -2522,8 +2779,8 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
                        } else
                                keep = j, drop = i;
  
@@ -439,7 +439,7 @@ diff --git a/flist.c b/flist.c
                                        rprintf(FINFO,
                                            "removing duplicate name %s from file list (%d)\n",
                                            f_name(file, fbuf), drop + flist->ndx_start);
-@@ -2537,7 +2794,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+@@ -2545,7 +2802,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
        }
        flist->high = prev_i;
  
@@ -529,7 +529,7 @@ diff --git a/generator.c b/generator.c
                }
  
                statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
-@@ -1776,7 +1785,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1775,7 +1784,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                ;
        else if (fnamecmp_type == FNAMECMP_FUZZY)
                ;
@@ -687,7 +687,7 @@ diff --git a/options.c b/options.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -682,6 +682,10 @@ extern int xattrs_ndx;
+@@ -686,6 +686,10 @@ extern int xattrs_ndx;
  #define F_SUM(f) ((char*)OPT_EXTRA(f, LEN64_BUMP(f) + HLINK_BUMP(f) \
                                    + SUM_EXTRA_CNT - 1))
  
@@ -698,7 +698,7 @@ diff --git a/rsync.h b/rsync.h
  /* Some utility defines: */
  #define F_IS_ACTIVE(f) (f)->basename[0]
  #define F_IS_HLINKED(f) ((f)->flags & FLAG_HLINKED)
-@@ -860,6 +864,13 @@ typedef struct {
+@@ -864,6 +868,13 @@ typedef struct {
        char fname[1]; /* has variable size */
  } relnamecache;
  
index e4632c1..20e9bcd 100644 (file)
@@ -52,7 +52,7 @@ diff --git a/flist.c b/flist.c
  } *csum_cache = NULL;
  
  static void flist_sort_and_clean(struct file_list *flist, int flags);
-@@ -363,7 +372,79 @@ static void flist_done_allocating(struct file_list *flist)
+@@ -351,7 +360,79 @@ static void flist_done_allocating(struct file_list *flist)
                flist->pool_boundary = ptr;
  }
  
@@ -133,7 +133,7 @@ diff --git a/flist.c b/flist.c
  {
        int slot, slots = am_sender ? 1 : basis_dir_cnt + 1;
  
-@@ -377,6 +458,9 @@ void reset_checksum_cache()
+@@ -365,6 +446,9 @@ void reset_checksum_cache()
                struct file_list *flist = csum_cache[slot].flist;
  
                if (flist) {
@@ -143,7 +143,7 @@ diff --git a/flist.c b/flist.c
                        /* Reset the pool memory and empty the file-list array. */
                        pool_free_old(flist->file_pool,
                                      pool_boundary(flist->file_pool, 0));
-@@ -387,6 +471,10 @@ void reset_checksum_cache()
+@@ -375,6 +459,10 @@ void reset_checksum_cache()
                flist->low = 0;
                flist->high = -1;
                flist->next = NULL;
@@ -154,7 +154,7 @@ diff --git a/flist.c b/flist.c
        }
  }
  
-@@ -394,7 +482,7 @@ void reset_checksum_cache()
+@@ -382,7 +470,7 @@ void reset_checksum_cache()
  static int add_checksum(struct file_list *flist, const char *dirname,
                        const char *basename, int basename_len, OFF_T file_length,
                        time_t mtime, uint32 ctime, uint32 inode,
@@ -163,7 +163,7 @@ diff --git a/flist.c b/flist.c
  {
        struct file_struct *file;
        int alloc_len, extra_len;
-@@ -411,7 +499,7 @@ static int add_checksum(struct file_list *flist, const char *dirname,
+@@ -399,7 +487,7 @@ static int add_checksum(struct file_list *flist, const char *dirname,
        if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
                extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
  #endif
@@ -172,7 +172,7 @@ diff --git a/flist.c b/flist.c
        bp = pool_alloc(flist->file_pool, alloc_len, "add_checksum");
  
        memset(bp, 0, extra_len + FILE_STRUCT_LEN);
-@@ -420,7 +508,14 @@ static int add_checksum(struct file_list *flist, const char *dirname,
+@@ -408,7 +496,14 @@ static int add_checksum(struct file_list *flist, const char *dirname,
        bp += FILE_STRUCT_LEN;
  
        memcpy(bp, basename, basename_len);
@@ -187,7 +187,7 @@ diff --git a/flist.c b/flist.c
        file->mode = S_IFREG;
        file->modtime = mtime;
        file->len32 = (uint32)file_length;
-@@ -449,10 +544,11 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
+@@ -437,10 +532,11 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
        char line[MAXPATHLEN+1024], fbuf[MAXPATHLEN], sum[MAX_DIGEST_LEN];
        FILE *fp;
        char *cp;
@@ -200,7 +200,7 @@ diff --git a/flist.c b/flist.c
        int dlen = dirname ? strlcpy(fbuf, dirname, sizeof fbuf) : 0;
  
        if (dlen >= (int)(sizeof fbuf - 1 - RSYNCSUMS_LEN))
-@@ -473,7 +569,7 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
+@@ -461,7 +557,7 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
        while (fgets(line, sizeof line, fp)) {
                cp = line;
                if (protocol_version >= 30) {
@@ -209,7 +209,7 @@ diff --git a/flist.c b/flist.c
                        if (*cp == '=')
                                while (*++cp == '=') {}
                        else
-@@ -484,7 +580,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
+@@ -472,7 +568,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
                }
  
                if (*cp == '=') {
@@ -225,7 +225,7 @@ diff --git a/flist.c b/flist.c
                } else {
                        for (i = 0; i < checksum_len*2; i++, cp++) {
                                int x;
-@@ -502,13 +605,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
+@@ -490,13 +593,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
                                else
                                        sum[i/2] = x << 4;
                        }
@@ -241,7 +241,7 @@ diff --git a/flist.c b/flist.c
                        if (*cp == '=')
                                while (*++cp == '=') {}
                        else
-@@ -558,24 +662,112 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
+@@ -546,24 +650,112 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
                        continue;
  
                strlcpy(fbuf+dlen, cp, sizeof fbuf - dlen);
@@ -356,7 +356,7 @@ diff --git a/flist.c b/flist.c
                read_checksums(slot, flist, file->dirname);
        }
  
-@@ -587,12 +779,31 @@ void get_cached_checksum(int slot, const char *fname, struct file_struct *file,
+@@ -575,12 +767,31 @@ void get_cached_checksum(int slot, const char *fname, struct file_struct *file,
                 && (checksum_files & CSF_LAX
                  || (F_CTIME(fp) == (uint32)stp->st_ctime
                   && F_INODE(fp) == (uint32)stp->st_ino))) {
@@ -388,9 +388,9 @@ diff --git a/flist.c b/flist.c
 +      }
  }
  
- int push_pathname(const char *dir, int len)
-@@ -1349,6 +1560,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
-       if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
+ /* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
+@@ -1360,6 +1571,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+       if (excl_ret) {
                if (ignore_perishable)
                        non_perishable_cnt++;
 +              if (S_ISREG(st.st_mode))
@@ -398,7 +398,7 @@ diff --git a/flist.c b/flist.c
                return NULL;
        }
  
-@@ -1395,13 +1608,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1406,13 +1619,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                        lastdir[len] = '\0';
                        lastdir_len = len;
                        if (checksum_files && am_sender && flist)
@@ -414,7 +414,7 @@ diff --git a/flist.c b/flist.c
                }
        }
        basename_len = strlen(basename) + 1; /* count the '\0' */
-@@ -1483,7 +1696,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1494,7 +1707,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
  
        if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
                if (flist && checksum_files)
@@ -423,7 +423,7 @@ diff --git a/flist.c b/flist.c
                else
                        file_checksum(thisname, st.st_size, tmp_sum);
        }
-@@ -1809,6 +2022,9 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
+@@ -1820,6 +2033,9 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
  
        closedir(d);
  
@@ -433,7 +433,7 @@ diff --git a/flist.c b/flist.c
        if (f >= 0 && recurse && !divert_dirs) {
                int i, end = flist->used - 1;
                /* send_if_directory() bumps flist->used, so use "end". */
-@@ -2405,6 +2621,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
+@@ -2413,6 +2629,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                }
        } else
                flist_eof = 1;
@@ -473,15 +473,15 @@ diff --git a/generator.c b/generator.c
                        }
                        need_new_dirscan = 0;
                }
-@@ -1499,6 +1501,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1498,6 +1500,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+                       else
                                change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
                }
 +              upcoming_whole_dir = file->flags & FLAG_CONTENT_DIR && f_out != -1 ? 1 : 0;
                goto cleanup;
        }
  
-@@ -1791,6 +1794,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1790,6 +1793,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        handle_partial_dir(partialptr, PDIR_DELETE);
                }
                set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
@@ -490,7 +490,7 @@ diff --git a/generator.c b/generator.c
                if (itemizing)
                        itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
  #ifdef SUPPORT_HARD_LINKS
-@@ -2205,6 +2210,7 @@ void generate_files(int f_out, const char *local_name)
+@@ -2204,6 +2209,7 @@ void generate_files(int f_out, const char *local_name)
                                } else
                                        change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
                        }
@@ -498,7 +498,7 @@ diff --git a/generator.c b/generator.c
                }
                for (i = cur_flist->low; i <= cur_flist->high; i++) {
                        struct file_struct *file = cur_flist->sorted[i];
-@@ -2285,6 +2291,9 @@ void generate_files(int f_out, const char *local_name)
+@@ -2284,6 +2290,9 @@ void generate_files(int f_out, const char *local_name)
                        wait_for_receiver();
        }
  
@@ -595,7 +595,7 @@ diff --git a/receiver.c b/receiver.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -866,6 +866,8 @@ typedef struct {
+@@ -870,6 +870,8 @@ typedef struct {
  
  #define CSF_ENABLE (1<<1)
  #define CSF_LAX (1<<2)
index f2d76a6..88c2e94 100644 (file)
@@ -11,7 +11,7 @@ To use this patch, run these commands for a successful build:
 diff --git a/flist.c b/flist.c
 --- a/flist.c
 +++ b/flist.c
-@@ -1224,7 +1224,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1235,7 +1235,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                memcpy(bp + basename_len, linkname, linkname_len);
  #endif
  
index 3ebc62d..60aca11 100644 (file)
@@ -19,7 +19,7 @@ diff --git a/generator.c b/generator.c
  extern int preserve_specials;
  extern int preserve_hard_links;
  extern int preserve_executability;
-@@ -1671,7 +1672,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1670,7 +1671,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                goto cleanup;
        }
  
index c92b428..1d50bc9 100644 (file)
@@ -48,7 +48,7 @@ diff --git a/flist.c b/flist.c
  extern int relative_paths;
  extern int implied_dirs;
  extern int file_extra_cnt;
-@@ -380,7 +381,7 @@ int push_pathname(const char *dir, int len)
+@@ -385,7 +386,7 @@ int change_pathname(struct file_struct *file, const char *dir, int dirlen)
  
  static void send_file_entry(int f, const char *fname, struct file_struct *file, int ndx, int first_ndx)
  {
@@ -57,7 +57,7 @@ diff --git a/flist.c b/flist.c
        static mode_t mode;
  #ifdef SUPPORT_FILEFLAGS
        static uint32 fileflags;
-@@ -469,6 +470,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+@@ -470,6 +471,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                xflags |= XMIT_SAME_TIME;
        else
                modtime = file->modtime;
@@ -118,7 +118,7 @@ diff --git a/flist.c b/flist.c
        if (unsort_ndx)
                F_NDX(file) = flist->used + flist->ndx_start;
  
-@@ -1246,6 +1271,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1257,6 +1282,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                F_OWNER(file) = st.st_uid;
        if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
                F_GROUP(file) = st.st_gid;
@@ -338,7 +338,7 @@ diff --git a/rsync.h b/rsync.h
  #define XMIT_SAME_FLAGS (1<<14)               /* protocols ?? - now */
  
  /* These flags are used in the live flist data. */
-@@ -153,6 +154,7 @@
+@@ -154,6 +155,7 @@
  #define ATTRS_REPORT          (1<<0)
  #define ATTRS_SKIP_MTIME      (1<<1)
  #define ATTRS_DELAY_IMMUTABLE (1<<2)
@@ -346,7 +346,7 @@ diff --git a/rsync.h b/rsync.h
  
  #define FULL_FLUSH    1
  #define NORMAL_FLUSH  0
-@@ -169,7 +171,7 @@
+@@ -170,7 +172,7 @@
  #define FNAMECMP_FUZZY                0x83
  
  /* For use by the itemize_changes code */
@@ -355,7 +355,7 @@ diff --git a/rsync.h b/rsync.h
  #define ITEM_REPORT_CHANGE (1<<1)
  #define ITEM_REPORT_SIZE (1<<2)     /* regular files only */
  #define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
-@@ -647,6 +649,7 @@ extern int file_extra_cnt;
+@@ -651,6 +653,7 @@ extern int file_extra_cnt;
  extern int inc_recurse;
  extern int uid_ndx;
  extern int gid_ndx;
@@ -363,7 +363,7 @@ diff --git a/rsync.h b/rsync.h
  extern int fileflags_ndx;
  extern int acls_ndx;
  extern int xattrs_ndx;
-@@ -654,6 +657,7 @@ extern int xattrs_ndx;
+@@ -658,6 +661,7 @@ extern int xattrs_ndx;
  #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
  #define EXTRA_LEN (sizeof (union file_extras))
  #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
@@ -371,7 +371,7 @@ diff --git a/rsync.h b/rsync.h
  #define DEV_EXTRA_CNT 2
  #define DIRNODE_EXTRA_CNT 3
  #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
-@@ -912,6 +916,7 @@ typedef struct {
+@@ -916,6 +920,7 @@ typedef struct {
  
  typedef struct {
      STRUCT_STAT st;
index 4da813e..0fb1fbe 100644 (file)
@@ -88,7 +88,7 @@ diff --git a/exclude.c b/exclude.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -148,6 +148,7 @@
+@@ -149,6 +149,7 @@
  #define XFLG_ANCHORED2ABS     (1<<2) /* leading slash indicates absolute */
  #define XFLG_ABS_IF_SLASH     (1<<3) /* leading or interior slash is absolute */
  #define XFLG_DIR2WILD3                (1<<4) /* dir/ match gets trailing *** added */
diff --git a/db.diff b/db.diff
new file mode 100644 (file)
index 0000000..6df344c
--- /dev/null
+++ b/db.diff
@@ -0,0 +1,1215 @@
+Added some DB-access routines to help rsync keep extra filesystem info
+about the files it is dealing with.  This adds both the --db=CONFIG_FILE
+option and the "db config" daemon parameter.
+
+For the moment this only adds checksum caching when the --checksum option
+is used.  Future improvments may include:
+
+ - Updating of MD5 checksums when transferring any file, even w/o -c.
+
+ - Caching of path info that allows for the finding of files to use for
+   moving/linking/copying/alternate-basis-use.
+
+ - Extend DB support beyond MySQL and SQLite (PostgreSQL?).
+
+To use this patch, run these commands for a successful build:
+
+    patch -p1 <patches/remote-option.diff
+    patch -p1 <patches/db.diff
+    ./configure                               (optional if already run)
+    make
+
+diff --git a/Makefile.in b/Makefile.in
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -35,7 +35,7 @@ ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
+ OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
+       util.o main.o checksum.o match.o syscall.o log.o backup.o
+ OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
+-      fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
++      fileio.o batch.o clientname.o chmod.o db.o acls.o xattrs.o
+ OBJS3=progress.o pipe.o
+ DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
+ popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
+diff --git a/checksum.c b/checksum.c
+--- a/checksum.c
++++ b/checksum.c
+@@ -23,6 +23,7 @@
+ extern int checksum_seed;
+ extern int protocol_version;
++extern int use_db;
+ int csum_length = SHORT_SUM_LENGTH; /* initial value */
+@@ -100,10 +101,10 @@ void get_checksum2(char *buf, int32 len, char *sum)
+       }
+ }
+-void file_checksum(char *fname, char *sum, OFF_T size)
++void file_checksum(const char *fname, STRUCT_STAT *st_p, char *sum)
+ {
+       struct map_struct *buf;
+-      OFF_T i, len = size;
++      OFF_T i, len = st_p->st_size;
+       md_context m;
+       int32 remainder;
+       int fd;
+@@ -114,7 +115,7 @@ void file_checksum(char *fname, char *sum, OFF_T size)
+       if (fd == -1)
+               return;
+-      buf = map_file(fd, size, MAX_MAP_SIZE, CSUM_CHUNK);
++      buf = map_file(fd, len, MAX_MAP_SIZE, CSUM_CHUNK);
+       if (protocol_version >= 30) {
+               md5_begin(&m);
+@@ -148,6 +149,9 @@ void file_checksum(char *fname, char *sum, OFF_T size)
+               mdfour_result(&m, (uchar *)sum);
+       }
++      if (use_db)
++              db_set_checksum(fname, st_p, sum);
++
+       close(fd);
+       unmap_file(buf);
+ }
+diff --git a/cleanup.c b/cleanup.c
+--- a/cleanup.c
++++ b/cleanup.c
+@@ -27,6 +27,7 @@ extern int am_daemon;
+ extern int io_error;
+ extern int keep_partial;
+ extern int got_xfer_error;
++extern int use_db;
+ extern char *partial_dir;
+ extern char *logfile_name;
+@@ -124,6 +125,12 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
+               /* FALLTHROUGH */
+ #include "case_N.h"
++              if (use_db)
++                      db_disconnect();
++
++              /* FALLTHROUGH */
++#include "case_N.h"
++
+               if (cleanup_child_pid != -1) {
+                       int status;
+                       int pid = wait_process(cleanup_child_pid, &status, WNOHANG);
+diff --git a/clientserver.c b/clientserver.c
+--- a/clientserver.c
++++ b/clientserver.c
+@@ -42,6 +42,7 @@ extern int numeric_ids;
+ extern int filesfrom_fd;
+ extern int remote_protocol;
+ extern int protocol_version;
++extern int always_checksum;
+ extern int io_timeout;
+ extern int no_detach;
+ extern int write_batch;
+@@ -49,6 +50,7 @@ extern int default_af_hint;
+ extern int logfile_format_has_i;
+ extern int logfile_format_has_o_or_i;
+ extern mode_t orig_umask;
++extern char *db_config;
+ extern char *bind_address;
+ extern char *sockopts;
+ extern char *config_file;
+@@ -782,6 +784,12 @@ static int rsync_module(int f_in, int f_out, int i, char *addr, char *host)
+       } else if (am_root < 0) /* Treat --fake-super from client as --super. */
+               am_root = 2;
++      db_config = lp_db_config(i);
++      if (!*db_config || (!always_checksum && protocol_version < 30))
++              db_config = NULL;
++      else
++              db_read_config(FLOG, db_config);
++
+       if (filesfrom_fd == 0)
+               filesfrom_fd = f_in;
+diff --git a/configure.in b/configure.in
+--- a/configure.in
++++ b/configure.in
+@@ -969,6 +969,8 @@ if test x"$enable_acl_support" = x"no" -o x"$enable_xattr_support" = x"no" -o x"
+     fi
+ fi
++LIBS="$LIBS -lmysqlclient -lsqlite3"
++
+ case "$CC" in
+ ' checker'*|checker*)
+     AC_DEFINE(FORCE_FD_ZERO_MEMSET, 1, [Used to make "checker" understand that FD_ZERO() clears memory.])
+diff --git a/db.c b/db.c
+new file mode 100644
+--- /dev/null
++++ b/db.c
+@@ -0,0 +1,557 @@
++/*
++ * Routines to access extended file info via DB.
++ *
++ * Copyright (C) 2008 Wayne Davison
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this program; if not, visit the http://fsf.org website.
++ */
++
++#include "rsync.h"
++#include "ifuncs.h"
++
++#define USE_MYSQL
++#define USE_SQLITE
++
++#ifdef USE_MYSQL
++#include <mysql/mysql.h>
++#include <mysql/errmsg.h>
++#endif
++#ifdef USE_SQLITE
++#include <sqlite3.h>
++#endif
++
++extern int protocol_version;
++extern int checksum_len;
++
++#define DB_TYPE_NONE 0
++#define DB_TYPE_MYSQL 1
++#define DB_TYPE_SQLITE 2
++
++int use_db = DB_TYPE_NONE;
++
++static const char *dbhost = NULL, *dbuser = NULL, *dbpass = NULL, *dbname = NULL;
++static unsigned int dbport = 0;
++
++static union {
++#ifdef USE_MYSQL
++    MYSQL *mysql;
++#endif
++#ifdef USE_SQLITE
++    sqlite3 *sqlite;
++#endif
++    void *all;
++} dbh;
++
++#define SEL_DEV 0
++#define SEL_SUM 1
++#define REP_SUM 2
++#define MAX_PREP_CNT 3
++
++static union {
++#ifdef USE_MYSQL
++    MYSQL_STMT *mysql;
++#endif
++#ifdef USE_SQLITE
++    sqlite3_stmt *sqlite;
++#endif
++    void *all;
++} statements[MAX_PREP_CNT];
++
++static int md_num;
++static enum logcode log_code;
++
++static unsigned int bind_disk_id;
++static unsigned long long bind_devno, bind_ino, bind_size, bind_mtime, bind_ctime;
++static char bind_thishost[256], bind_sum[MAX_DIGEST_LEN];
++static int bind_thishost_len;
++
++static unsigned int prior_disk_id = 0;
++static unsigned long long prior_devno = 0;
++
++int db_read_config(enum logcode code, const char *config_file)
++{
++      char buf[2048], *cp;
++      FILE *fp;
++      int lineno = 0;
++
++      log_code = code;
++
++      bind_thishost_len = strlcpy(bind_thishost, "localhost", sizeof bind_thishost);
++
++      if (!(fp = fopen(config_file, "r"))) {
++              rsyserr(log_code, errno, "unable to open %s", config_file);
++              return 0;
++      }
++      while (fgets(buf, sizeof buf, fp)) {
++              lineno++;
++              if ((cp = strchr(buf, '#')) == NULL
++               && (cp = strchr(buf, '\r')) == NULL
++               && (cp = strchr(buf, '\n')) == NULL)
++                      cp = buf + strlen(buf);
++              while (cp != buf && isSpace(cp-1)) cp--;
++              *cp = '\0';
++
++              if (!*buf)
++                      continue;
++
++              if (!(cp = strchr(buf, ':')))
++                      goto invalid_line;
++              *cp++ = '\0';
++
++              while (isSpace(cp)) cp++;
++              if (strcasecmp(buf, "dbhost") == 0)
++                      dbhost = strdup(cp);
++              else if (strcasecmp(buf, "dbuser") == 0)
++                      dbuser = strdup(cp);
++              else if (strcasecmp(buf, "dbpass") == 0)
++                      dbpass = strdup(cp);
++              else if (strcasecmp(buf, "dbname") == 0)
++                      dbname = strdup(cp);
++              else if (strcasecmp(buf, "dbport") == 0)
++                      dbport = atoi(cp);
++              else if (strcasecmp(buf, "thishost") == 0)
++                      bind_thishost_len = strlcpy(bind_thishost, cp, sizeof bind_thishost);
++              else if (strcasecmp(buf, "dbtype") == 0) {
++#ifdef USE_MYSQL
++                      if (strcasecmp(cp, "mysql") == 0) {
++                              use_db = DB_TYPE_MYSQL;
++                              continue;
++                      }
++#endif
++#ifdef USE_SQLITE
++                      if (strcasecmp(cp, "sqlite") == 0) {
++                              use_db = DB_TYPE_SQLITE;
++                              continue;
++                      }
++#endif
++                      rprintf(log_code,
++                          "Unsupported dbtype on line #%d in %s.\n",
++                          lineno, config_file);
++                      use_db = DB_TYPE_NONE;
++                      return 0;
++              } else {
++                invalid_line:
++                      rprintf(log_code, "Invalid line #%d in %s\n",
++                              lineno, config_file);
++                      use_db = DB_TYPE_NONE;
++                      return 0;
++              }
++      }
++      fclose(fp);
++
++      if (bind_thishost_len >= (int)sizeof bind_thishost)
++              bind_thishost_len = sizeof bind_thishost - 1;
++
++      if (!use_db || !dbname) {
++              rprintf(log_code, "Please specify at least dbtype and dbname in %s.\n", config_file);
++              use_db = DB_TYPE_NONE;
++              return 0;
++      }
++
++      md_num = protocol_version >= 30 ? 5 : 4;
++
++      return 1;
++}
++
++#ifdef USE_MYSQL
++static MYSQL_STMT *prepare_mysql(MYSQL_BIND *binds, int bind_cnt, const char *fmt, ...)
++{
++      va_list ap;
++      char *query;
++      int qlen, param_cnt;
++      MYSQL_STMT *stmt = mysql_stmt_init(dbh.mysql);
++
++      if (stmt == NULL)
++              out_of_memory("prepare_mysql");
++
++      va_start(ap, fmt);
++      qlen = vasprintf(&query, fmt, ap);
++      va_end(ap);
++      if (qlen < 0)
++              out_of_memory("prepare_mysql");
++
++      if (mysql_stmt_prepare(stmt, query, qlen) != 0) {
++              rprintf(log_code, "Prepare failed: %s\n", mysql_stmt_error(stmt));
++              return NULL;
++      }
++      free(query);
++
++      if ((param_cnt = mysql_stmt_param_count(stmt)) != bind_cnt) {
++              rprintf(log_code, "Parameters in statement = %d, bind vars = %d\n",
++                      param_cnt, bind_cnt);
++              return NULL;
++      }
++      if (bind_cnt)
++              mysql_stmt_bind_param(stmt, binds);
++
++      return stmt;
++}
++#endif
++
++#ifdef USE_MYSQL
++static int db_connect_mysql(void)
++{
++      MYSQL_BIND binds[10];
++
++      if (!(dbh.mysql = mysql_init(NULL)))
++              out_of_memory("db_read_config");
++
++      if (!mysql_real_connect(dbh.mysql, dbhost, dbuser, dbpass, dbname, dbport, NULL, 0))
++              return 0;
++
++      memset(binds, 0, sizeof binds);
++      binds[0].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[0].buffer = &bind_devno;
++      binds[1].buffer_type = MYSQL_TYPE_STRING;
++      binds[1].buffer = &bind_thishost;
++      binds[1].buffer_length = bind_thishost_len;
++      statements[SEL_DEV].mysql = prepare_mysql(binds, 2,
++              "SELECT disk_id"
++              " FROM disk"
++              " WHERE devno = ? AND host = ? AND mounted = 1");
++      if (!statements[SEL_DEV].mysql)
++              return 0;
++
++      memset(binds, 0, sizeof binds);
++      binds[0].buffer_type = MYSQL_TYPE_LONG;
++      binds[0].buffer = &bind_disk_id;
++      binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[1].buffer = &bind_ino;
++      binds[2].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[2].buffer = &bind_size;
++      binds[3].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[3].buffer = &bind_mtime;
++      binds[4].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[4].buffer = &bind_ctime;
++      statements[SEL_SUM].mysql = prepare_mysql(binds, 5,
++              "SELECT checksum"
++              " FROM inode_map"
++              " WHERE disk_id = ? AND ino = ? AND sum_type = %d"
++              "   AND size = ? AND mtime = ? AND ctime = ?",
++              md_num);
++      if (!statements[SEL_SUM].mysql)
++              return 0;
++
++      memset(binds, 0, sizeof binds);
++      binds[0].buffer_type = MYSQL_TYPE_LONG;
++      binds[0].buffer = &bind_disk_id;
++      binds[1].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[1].buffer = &bind_ino;
++      binds[2].buffer_type = binds[6].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[2].buffer = binds[6].buffer = &bind_size;
++      binds[3].buffer_type = binds[7].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[3].buffer = binds[7].buffer = &bind_mtime;
++      binds[4].buffer_type = binds[8].buffer_type = MYSQL_TYPE_LONGLONG;
++      binds[4].buffer = binds[8].buffer = &bind_ctime;
++      binds[5].buffer_type = binds[9].buffer_type = MYSQL_TYPE_BLOB;
++      binds[5].buffer = binds[9].buffer = &bind_sum;
++      binds[5].buffer_length = binds[9].buffer_length = checksum_len;
++      statements[REP_SUM].mysql = prepare_mysql(binds, 10,
++              "INSERT INTO inode_map"
++              " SET disk_id = ?, ino = ?, sum_type = %d,"
++              "     size = ?, mtime = ?, ctime = ?, checksum = ?"
++              " ON DUPLICATE KEY"
++              " UPDATE size = ?, mtime = ?, ctime = ?, checksum = ?",
++              md_num, md_num);
++      if (!statements[REP_SUM].mysql)
++              return 0;
++
++      return 1;
++}
++#endif
++
++#ifdef USE_SQLITE
++static int db_connect_sqlite(void)
++{
++      char *sql;
++
++#if 0
++      if (sqlite3_open_v2(dbname, &dbh.sqlite, SQLITE_OPEN_READWRITE, NULL) != 0)
++              return 0;
++#else
++      if (sqlite3_open(dbname, &dbh.sqlite) != 0)
++              return 0;
++#endif
++
++      sql = "SELECT disk_id"
++          " FROM disk"
++          " WHERE devno = ? AND host = ? AND mounted = 1";
++      if (sqlite3_prepare_v2(dbh.sqlite, sql, -1, &statements[SEL_DEV].sqlite, NULL) != 0)
++              return 0;
++
++      if (asprintf(&sql,
++          "SELECT checksum"
++          " FROM inode_map"
++          " WHERE disk_id = ? AND ino = ? AND sum_type = %d"
++          "   AND size = ? AND mtime = ? AND ctime = ?",
++          md_num) < 0
++       || sqlite3_prepare_v2(dbh.sqlite, sql, -1, &statements[SEL_SUM].sqlite, NULL) != 0)
++              return 0;
++      free(sql);
++
++      if (asprintf(&sql,
++          "INSERT OR REPLACE INTO inode_map"
++          " (disk_id, ino, sum_type, size, mtime, ctime, checksum)"
++          " VALUES(?, ?, %d, ?, ?, ?, ?)",
++          md_num) < 0
++       || sqlite3_prepare_v2(dbh.sqlite, sql, -1, &statements[REP_SUM].sqlite, NULL) != 0)
++              return 0;
++      free(sql);
++
++      return 1;
++}
++#endif
++
++int db_connect(void)
++{
++      switch (use_db) {
++#ifdef USE_MYSQL
++      case DB_TYPE_MYSQL:
++              if (db_connect_mysql())
++                      return 1;
++              break;
++#endif
++#ifdef USE_SQLITE
++      case DB_TYPE_SQLITE:
++              if (db_connect_sqlite())
++                      return 1;
++              break;
++#endif
++      }
++
++      rprintf(log_code, "Unable to connect to DB\n");
++      db_disconnect();
++      use_db = DB_TYPE_NONE;
++
++      return 0;
++}
++
++void db_disconnect(void)
++{
++      int ndx;
++
++      if (!dbh.all)
++              return;
++
++      for (ndx = 0; ndx < MAX_PREP_CNT; ndx++) {
++              if (statements[ndx].all) {
++                      switch (use_db) {
++#ifdef USE_MYSQL
++                      case DB_TYPE_MYSQL:
++                              mysql_stmt_close(statements[ndx].mysql);
++                              break;
++#endif
++#ifdef USE_SQLITE
++                      case DB_TYPE_SQLITE:
++                              sqlite3_finalize(statements[ndx].sqlite);
++                              break;
++#endif
++                      }
++                      statements[ndx].all = NULL;
++              }
++      }
++
++      switch (use_db) {
++#ifdef USE_MYSQL
++      case DB_TYPE_MYSQL:
++              mysql_close(dbh.mysql);
++              break;
++#endif
++#ifdef USE_SQLITE
++      case DB_TYPE_SQLITE:
++              sqlite3_close(dbh.sqlite);
++              break;
++#endif
++      }
++
++      dbh.all = NULL;
++}
++
++static MYSQL_STMT *exec_mysql(int ndx)
++{
++      MYSQL_STMT *stmt = statements[ndx].mysql;
++      int rc;
++
++      if ((rc = mysql_stmt_execute(stmt)) == CR_SERVER_LOST) {
++              db_disconnect();
++              if (db_connect()) {
++                      stmt = statements[ndx].mysql;
++                      rc = mysql_stmt_execute(stmt);
++              }
++      }
++      if (rc != 0) {
++              rprintf(log_code, "SQL execute failed: %s\n", mysql_stmt_error(stmt));
++              return NULL;
++      }
++
++      return stmt;
++}
++
++static int fetch_mysql(MYSQL_BIND *binds, int bind_cnt, int ndx)
++{
++      unsigned long length[32];
++      my_bool is_null[32], error[32];
++      MYSQL_STMT *stmt;
++      int i, rc;
++
++      if (bind_cnt > 32)
++              exit_cleanup(RERR_UNSUPPORTED);
++
++      if ((stmt = exec_mysql(ndx)) == NULL)
++              return 0;
++
++      for (i = 0; i < bind_cnt; i++) {
++              binds[i].is_null = &is_null[i];
++              binds[i].length = &length[i];
++              binds[i].error = &error[i];
++      }
++      mysql_stmt_bind_result(stmt, binds);
++
++      if ((rc = mysql_stmt_fetch(stmt)) != 0) {
++              if (rc != MYSQL_NO_DATA) {
++                      rprintf(log_code, "SELECT fetch failed: %s\n",
++                              mysql_stmt_error(stmt));
++              }
++              mysql_stmt_free_result(stmt);
++              return 0;
++      }
++
++      mysql_stmt_free_result(stmt);
++
++      return is_null[0] ? 0 : 1;
++}
++
++static void get_disk_id(unsigned long long devno)
++{
++      switch (use_db) {
++#ifdef USE_MYSQL
++      case DB_TYPE_MYSQL: {
++              MYSQL_BIND binds[1];
++
++              bind_devno = devno; /* The one variable SEL_DEV input value. */
++
++              /* Bind where to put the output. */
++              binds[0].buffer_type = MYSQL_TYPE_LONG;
++              binds[0].buffer = &prior_disk_id;
++              if (!fetch_mysql(binds, 1, SEL_DEV))
++                      prior_disk_id = 0;
++              break;
++          }
++#endif
++#ifdef USE_SQLITE
++      case DB_TYPE_SQLITE: {
++              sqlite3_stmt *stmt = statements[SEL_DEV].sqlite;
++              sqlite3_bind_int64(stmt, 1, devno);
++              sqlite3_bind_text(stmt, 2, bind_thishost, bind_thishost_len, SQLITE_STATIC);
++              if (sqlite3_step(stmt) == SQLITE_ROW)
++                      prior_disk_id = sqlite3_column_int(stmt, 0);
++              else
++                      prior_disk_id = 0;
++              sqlite3_reset(stmt);
++              break;
++          }
++#endif
++      }
++
++      prior_devno = devno;
++}
++
++int db_get_checksum(UNUSED(const char *fname), const STRUCT_STAT *st_p, char *sum)
++{
++      if (prior_devno != st_p->st_dev)
++              get_disk_id(st_p->st_dev);
++      if (prior_disk_id == 0)
++              return 0;
++
++      switch (use_db) {
++#ifdef USE_MYSQL
++      case DB_TYPE_MYSQL: {
++              MYSQL_BIND binds[1];
++
++              bind_disk_id = prior_disk_id;
++              bind_ino = st_p->st_ino;
++              bind_size = st_p->st_size;
++              bind_mtime = st_p->st_mtime;
++              bind_ctime = st_p->st_ctime;
++
++              binds[0].buffer_type = MYSQL_TYPE_BLOB;
++              binds[0].buffer = sum;
++              binds[0].buffer_length = checksum_len;
++              return fetch_mysql(binds, 1, SEL_SUM);
++          }
++#endif
++#ifdef USE_SQLITE
++      case DB_TYPE_SQLITE: {
++              sqlite3_stmt *stmt = statements[SEL_SUM].sqlite;
++              sqlite3_bind_int(stmt, 1, prior_disk_id);
++              sqlite3_bind_int64(stmt, 2, st_p->st_ino);
++              sqlite3_bind_int64(stmt, 3, st_p->st_size);
++              sqlite3_bind_int64(stmt, 4, st_p->st_mtime);
++              sqlite3_bind_int64(stmt, 5, st_p->st_ctime);
++              if (sqlite3_step(stmt) == SQLITE_ROW) {
++                      int len = sqlite3_column_bytes(stmt, 0);
++                      if (len > MAX_DIGEST_LEN)
++                              len = MAX_DIGEST_LEN;
++                      memcpy(sum, sqlite3_column_blob(stmt, 0), len);
++                      sqlite3_reset(stmt);
++                      return 1;
++              }
++              sqlite3_reset(stmt);
++              return 0;
++          }
++#endif
++      }
++
++      return 0;
++}
++
++int db_set_checksum(UNUSED(const char *fname), const STRUCT_STAT *st_p, const char *sum)
++{
++      if (prior_devno != st_p->st_dev)
++              get_disk_id(st_p->st_dev);
++      if (prior_disk_id == 0)
++              return 0;
++
++      switch (use_db) {
++#ifdef USE_MYSQL
++      case DB_TYPE_MYSQL: {
++              bind_disk_id = prior_disk_id;
++              bind_ino = st_p->st_ino;
++              bind_size = st_p->st_size;
++              bind_mtime = st_p->st_mtime;
++              bind_ctime = st_p->st_ctime;
++              memcpy(bind_sum, sum, checksum_len);
++
++              return exec_mysql(REP_SUM) != NULL;
++          }
++#endif
++#ifdef USE_SQLITE
++      case DB_TYPE_SQLITE: {
++              int rc;
++              sqlite3_stmt *stmt = statements[REP_SUM].sqlite;
++              sqlite3_bind_int(stmt, 1, prior_disk_id);
++              sqlite3_bind_int64(stmt, 2, st_p->st_ino);
++              sqlite3_bind_int64(stmt, 3, st_p->st_size);
++              sqlite3_bind_int64(stmt, 4, st_p->st_mtime);
++              sqlite3_bind_int64(stmt, 5, st_p->st_ctime);
++              sqlite3_bind_blob(stmt, 6, sum, checksum_len, SQLITE_TRANSIENT);
++              rc = sqlite3_step(stmt);
++              sqlite3_reset(stmt);
++              return rc == SQLITE_DONE;
++          }
++#endif
++      }
++
++      return 0;
++}
+diff --git a/flist.c b/flist.c
+--- a/flist.c
++++ b/flist.c
+@@ -54,6 +54,7 @@ extern int preserve_devices;
+ extern int preserve_specials;
+ extern int uid_ndx;
+ extern int gid_ndx;
++extern int use_db;
+ extern int eol_nulls;
+ extern int relative_paths;
+ extern int implied_dirs;
+@@ -1235,14 +1236,16 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+               memcpy(bp + basename_len, linkname, linkname_len);
+ #endif
+-      if (always_checksum && am_sender && S_ISREG(st.st_mode))
+-              file_checksum(thisname, tmp_sum, st.st_size);
+-
+       if (am_sender)
+               F_PATHNAME(file) = pathname;
+       else if (!pool)
+               F_DEPTH(file) = extra_len / EXTRA_LEN;
++      if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
++              if (!use_db || !db_get_checksum(thisname, &st, tmp_sum))
++                      file_checksum(thisname, &st, tmp_sum);
++      }
++
+       /* This code is only used by the receiver when it is building
+        * a list of files for a delete pass. */
+       if (keep_dirlinks && linkname_len && flist) {
+@@ -1858,6 +1861,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
+                    | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
+       int implied_dot_dir = 0;
++      if (use_db)
++              db_connect();
++
+       rprintf(FLOG, "building file list\n");
+       if (show_filelist_p())
+               start_filelist_progress("building file list");
+diff --git a/generator.c b/generator.c
+--- a/generator.c
++++ b/generator.c
+@@ -58,6 +58,7 @@ extern int update_only;
+ extern int ignore_existing;
+ extern int ignore_non_existing;
+ extern int inplace;
++extern int use_db;
+ extern int append_mode;
+ extern int make_backups;
+ extern int csum_length;
+@@ -718,7 +719,8 @@ int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
+          of the file time to determine whether to sync */
+       if (always_checksum > 0 && S_ISREG(st->st_mode)) {
+               char sum[MAX_DIGEST_LEN];
+-              file_checksum(fn, sum, st->st_size);
++              if (!use_db || !db_get_checksum(fn, st, sum))
++                      file_checksum(fn, st, sum);
+               return memcmp(sum, F_SUM(file), checksum_len) == 0;
+       }
+@@ -2161,6 +2163,9 @@ void generate_files(int f_out, const char *local_name)
+                       : "enabled");
+       }
++      if (use_db && always_checksum)
++              db_connect();
++
+       /* Since we often fill up the outgoing socket and then just sit around
+        * waiting for the other 2 processes to do their thing, we don't want
+        * to exit on a timeout.  If the data stops flowing, the receiver will
+diff --git a/loadparm.c b/loadparm.c
+--- a/loadparm.c
++++ b/loadparm.c
+@@ -126,6 +126,7 @@ typedef struct
+       char *auth_users;
+       char *charset;
+       char *comment;
++      char *db_config;
+       char *dont_compress;
+       char *exclude;
+       char *exclude_from;
+@@ -177,6 +178,7 @@ static service sDefault =
+  /* auth_users; */            NULL,
+  /* charset; */               NULL,
+  /* comment; */               NULL,
++ /* db_config; */             NULL,
+  /* dont_compress; */         DEFAULT_DONT_COMPRESS,
+  /* exclude; */                       NULL,
+  /* exclude_from; */          NULL,
+@@ -307,6 +309,7 @@ static struct parm_struct parm_table[] =
+  {"auth users",        P_STRING, P_LOCAL, &sDefault.auth_users,        NULL,0},
+  {"charset",           P_STRING, P_LOCAL, &sDefault.charset,           NULL,0},
+  {"comment",           P_STRING, P_LOCAL, &sDefault.comment,           NULL,0},
++ {"db config",         P_STRING, P_LOCAL, &sDefault.db_config,         NULL,0},
+  {"dont compress",     P_STRING, P_LOCAL, &sDefault.dont_compress,     NULL,0},
+  {"exclude from",      P_STRING, P_LOCAL, &sDefault.exclude_from,      NULL,0},
+  {"exclude",           P_STRING, P_LOCAL, &sDefault.exclude,           NULL,0},
+@@ -400,6 +403,7 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Globals.rsync_port)
+ FN_LOCAL_STRING(lp_auth_users, auth_users)
+ FN_LOCAL_STRING(lp_charset, charset)
+ FN_LOCAL_STRING(lp_comment, comment)
++FN_LOCAL_STRING(lp_db_config, db_config)
+ FN_LOCAL_STRING(lp_dont_compress, dont_compress)
+ FN_LOCAL_STRING(lp_exclude, exclude)
+ FN_LOCAL_STRING(lp_exclude_from, exclude_from)
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -49,6 +49,7 @@ extern int copy_unsafe_links;
+ extern int keep_dirlinks;
+ extern int preserve_hard_links;
+ extern int protocol_version;
++extern int always_checksum;
+ extern int file_total;
+ extern int recurse;
+ extern int xfer_dirs;
+@@ -73,6 +74,7 @@ extern char *partial_dir;
+ extern char *dest_option;
+ extern char *basis_dir[];
+ extern char *rsync_path;
++extern char *db_config;
+ extern char *shell_cmd;
+ extern char *batch_name;
+ extern char *password_file;
+@@ -1482,6 +1484,9 @@ int main(int argc,char *argv[])
+               exit_cleanup(RERR_SYNTAX);
+       }
++      if (db_config && (always_checksum || protocol_version >= 30))
++              db_read_config(FERROR, db_config);
++
+       if (am_server) {
+               set_nonblocking(STDIN_FILENO);
+               set_nonblocking(STDOUT_FILENO);
+diff --git a/options.c b/options.c
+--- a/options.c
++++ b/options.c
+@@ -92,6 +92,7 @@ int use_qsort = 0;
+ char *files_from = NULL;
+ int filesfrom_fd = -1;
+ char *filesfrom_host = NULL;
++char *db_config = NULL;
+ int eol_nulls = 0;
+ int protect_args = 0;
+ int human_readable = 0;
+@@ -321,6 +322,7 @@ void usage(enum logcode F)
+   rprintf(F," -q, --quiet                 suppress non-error messages\n");
+   rprintf(F,"     --no-motd               suppress daemon-mode MOTD (see manpage caveat)\n");
+   rprintf(F," -c, --checksum              skip based on checksum, not mod-time & size\n");
++  rprintf(F,"     --db=CONFIG_FILE        specify a config file for FS DB\n");
+   rprintf(F," -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)\n");
+   rprintf(F,"     --no-OPTION             turn off an implied OPTION (e.g. --no-D)\n");
+   rprintf(F," -r, --recursive             recurse into directories\n");
+@@ -579,6 +581,7 @@ static struct poptOption long_options[] = {
+   {"checksum",        'c', POPT_ARG_VAL,    &always_checksum, 1, 0, 0 },
+   {"no-checksum",      0,  POPT_ARG_VAL,    &always_checksum, 0, 0, 0 },
+   {"no-c",             0,  POPT_ARG_VAL,    &always_checksum, 0, 0, 0 },
++  {"db",               0,  POPT_ARG_STRING, &db_config, 0, 0, 0 },
+   {"block-size",      'B', POPT_ARG_LONG,   &block_size, 0, 0, 0 },
+   {"compare-dest",     0,  POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
+   {"copy-dest",        0,  POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
+diff --git a/pipe.c b/pipe.c
+--- a/pipe.c
++++ b/pipe.c
+@@ -26,6 +26,10 @@ extern int am_sender;
+ extern int am_server;
+ extern int blocking_io;
+ extern int filesfrom_fd;
++extern int always_checksum;
++extern int protocol_version;
++extern int use_db;
++extern char *db_config;
+ extern mode_t orig_umask;
+ extern char *logfile_name;
+ extern int remote_option_cnt;
+@@ -141,6 +145,9 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
+                       logfile_close();
+               }
++              use_db = 0;
++              db_config = NULL;
++
+               if (remote_option_cnt) {
+                       int rc = remote_option_cnt + 1;
+                       const char **rv = remote_options;
+@@ -148,6 +155,8 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
+                               option_error();
+                               exit_cleanup(RERR_SYNTAX);
+                       }
++                      if (db_config && (always_checksum || protocol_version >= 30))
++                              db_read_config(FERROR, db_config);
+               }
+               if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
+diff --git a/receiver.c b/receiver.c
+--- a/receiver.c
++++ b/receiver.c
+@@ -43,11 +43,13 @@ extern int basis_dir_cnt;
+ extern int make_backups;
+ extern int cleanup_got_literal;
+ extern int remove_source_files;
++extern int always_checksum;
+ extern int append_mode;
+ extern int sparse_files;
+ extern int keep_partial;
+ extern int checksum_seed;
+ extern int inplace;
++extern int use_db;
+ extern int delay_updates;
+ extern mode_t orig_umask;
+ extern struct stats stats;
+@@ -399,6 +401,9 @@ int recv_files(int f_in, char *local_name)
+       if (verbose > 2)
+               rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);
++      if (use_db && !always_checksum)
++              db_connect();
++
+       if (delay_updates)
+               delayed_bits = bitbag_create(cur_flist->used + 1);
+diff --git a/support/dbupdate b/support/dbupdate
+new file mode 100755
+--- /dev/null
++++ b/support/dbupdate
+@@ -0,0 +1,281 @@
++#!/usr/bin/perl -w
++use strict;
++
++use DBI;
++use Getopt::Long;
++use Cwd qw(abs_path cwd);
++use Digest::MD4;
++use Digest::MD5;
++
++my $MOUNT_FILE = '/etc/mtab';
++
++&Getopt::Long::Configure('bundling');
++&usage if !&GetOptions(
++    'db=s' => \( my $db_config ),
++    'mounts|m' => \( my $update_mounts ),
++    'recurse|r' => \( my $recurse_opt ),
++    'check|c' => \( my $check_opt ),
++    'verbose|v+' => \( my $verbosity = 0 ),
++    'help|h' => \( my $help_opt ),
++);
++&usage if $help_opt || !defined $db_config;
++
++my %config;
++open(IN, '<', $db_config) or die "Unable to open $db_config: $!\n";
++while (<IN>) {
++    s/[#\r\n].*//s;
++    next if /^$/;
++    my($key, $val) = /^(\S+):\s*(.*)/ or die "Unable to parse line $. of $db_config\n";
++    $config{$key} = $val;
++}
++close IN;
++
++die "You must define at least dbtype and dbname in $db_config\n"
++    unless defined $config{'dbtype'} && defined $config{'dbname'};
++
++my $thishost = $config{'thishost'} || 'localhost';
++
++my $connect = 'DBI:' . $config{'dbtype'} . ':database=' . $config{'dbname'};
++$connect =~ s/:database=/:dbname=/ if $config{'dbtype'} eq 'SQLite';
++$connect .= ';host=' . $config{'dbhost'} if defined $config{'dbhost'};
++$connect .= ';port=' . $config{'dbport'} if defined $config{'dbport'};
++
++my $dbh = DBI->connect($connect, $config{'dbuser'}, $config{'dbpass'})
++    or die "DB connection failed\n";
++
++END {
++    $dbh->disconnect if defined $dbh;
++}
++
++my $sel_disk_H = $dbh->prepare("
++    SELECT disk_id, devno, mounted, comment
++    FROM disk
++    WHERE host = ?
++    ") or die $dbh->errstr;
++
++my $ins_disk_H = $dbh->prepare("
++    INSERT INTO disk
++    (devno, host, mounted, comment)
++    VALUES(?, ?, ?, ?)
++    ") or die $dbh->errstr;
++
++my $up_disk_H = $dbh->prepare("
++    UPDATE disk
++    SET mounted = ?
++    WHERE disk_id = ?
++    ") or die $dbh->errstr;
++
++my $row_id = $config{'dbtype'} eq 'SQLite' ? 'ROWID' : 'ID';
++my $sel_lastid_H = $dbh->prepare("
++    SELECT LAST_INSERT_$row_id()
++    ") or die $dbh->errstr;
++
++my $sel_sum_H = $dbh->prepare("
++    SELECT sum_type, checksum
++    FROM inode_map
++    WHERE disk_id = ? AND ino = ? AND size = ? AND mtime = ? AND ctime = ?
++    ") or die $dbh->errstr;
++
++my $rep_sum_H = $dbh->prepare("
++    REPLACE INTO inode_map
++    (disk_id, ino, size, mtime, ctime, sum_type, checksum)
++    VALUES(?, ?, ?, ?, ?, ?, ?)
++    ") or die $dbh->errstr;
++
++my %mounts;
++if ($update_mounts) {
++    open(IN, $MOUNT_FILE) or die "Unable to open $MOUNT_FILE: $!\n";
++    while (<IN>) {
++      my($devname, $mnt) = (split)[0,1];
++      next unless $devname =~ m#^/dev#;
++      my($devno) = (stat($mnt))[0];
++      if (!defined $devno) {
++          warn "Unable to stat $mnt: $!\n";
++          next;
++      }
++      $mounts{$devno} = "$devname on $mnt";
++    }
++    close IN;
++}
++
++my %disk_id;
++$sel_disk_H->execute($thishost);
++while (my($disk_id, $devno, $mounted, $comment) = $sel_disk_H->fetchrow_array) {
++    if ($update_mounts) {
++      if (defined $mounts{$devno}) {
++          if ($comment ne $mounts{$devno}) {
++              if ($mounted) {
++                  $up_disk_H->execute(0, $disk_id);
++              }
++              next;
++          }
++          if (!$mounted) {
++              $up_disk_H->execute(1, $disk_id);
++          }
++      } else {
++          if ($mounted) {
++              $up_disk_H->execute(0, $disk_id);
++          }
++          next;
++      }
++    } else {
++      next unless $mounted;
++    }
++    $disk_id{$devno} = $disk_id;
++}
++$sel_disk_H->finish;
++
++if ($update_mounts) {
++    while (my($devno, $comment) = each %mounts) {
++      next if $disk_id{$devno};
++      $ins_disk_H->execute($devno, $thishost, 1, $comment);
++      $sel_lastid_H->execute;
++      ($disk_id{$devno}) = $sel_lastid_H->fetchrow_array;
++      $sel_lastid_H->finish;
++    }
++}
++
++my $start_dir = cwd();
++
++my @dirs = @ARGV;
++@dirs = '.' unless @dirs;
++foreach (@dirs) {
++    $_ = abs_path($_);
++}
++
++$| = 1;
++
++my $exit_code = 0;
++
++my $md4 = Digest::MD4->new;
++my $md5 = Digest::MD5->new;
++
++while (@dirs) {
++    my $dir = shift @dirs;
++
++    if (!chdir($dir)) {
++      warn "Unable to chdir to $dir: $!\n";
++      next;
++    }
++    if (!opendir(DP, '.')) {
++      warn "Unable to opendir $dir: $!\n";
++      next;
++    }
++
++    my $reldir = $dir;
++    $reldir =~ s#^$start_dir(/|$)# $1 ? '' : '.' #eo;
++    print "$reldir ... \n" if $verbosity;
++
++    my @subdirs;
++    while (defined(my $fn = readdir(DP))) {
++      next if $fn =~ /^\.\.?$/ || -l $fn;
++      if (-d _) {
++          push(@subdirs, "$dir/$fn") unless $fn =~ /^(CVS|\.svn|\.git|\.bzr)$/;
++          next;
++      }
++      next unless -f _;
++
++      my($dev,$ino,$size,$mtime,$ctime) = (stat(_))[0,1,7,9,10];
++      my $disk_id = $disk_id{$dev} or next;
++      $sel_sum_H->execute($disk_id,$ino,$size,$mtime,$ctime) or die $!;
++      my($sum4, $dbsum4, $sum5, $dbsum5);
++      my $dbsumcnt = 0;
++      while (my($sum_type, $checksum) = $sel_sum_H->fetchrow_array) {
++          if ($sum_type == 4) {
++              $dbsum4 = $checksum;
++              $dbsumcnt++;
++          } elsif ($sum_type == 5) {
++              $dbsum5 = $checksum;
++              $dbsumcnt++;
++          }
++      }
++      $sel_sum_H->finish;
++
++      next if !$check_opt && $dbsumcnt == 2;
++
++      if (!$check_opt || $dbsumcnt || $verbosity > 2) {
++          if (!open(IN, $fn)) {
++              print STDERR "Unable to read $fn: $!\n";
++              next;
++          }
++
++          while (1) {
++              while (sysread(IN, $_, 64*1024)) {
++                  $md4->add($_);
++                  $md5->add($_);
++              }
++              $sum4 = $md4->digest;
++              $sum5 = $md5->digest;
++              print ' ', unpack('H*', $sum4), ' ', unpack('H*', $sum5) if $verbosity > 2;
++              print " $fn" if $verbosity > 1;
++              my($ino2,$size2,$mtime2,$ctime2) = (stat(IN))[1,7,9,10];
++              last if $ino == $ino2 && $size == $size2 && $mtime == $mtime2 && $ctime == $ctime2;
++              $ino = $ino2;
++              $size = $size2;
++              $mtime = $mtime2;
++              $ctime = $ctime2;
++              sysseek(IN, 0, 0);
++              print " REREADING\n" if $verbosity > 1;
++          }
++
++          close IN;
++      } elsif ($verbosity > 1) {
++          print "_$fn";
++      }
++
++      if ($check_opt) {
++          my $dif;
++          if ($dbsumcnt == 0) {
++              $dif = ' --MISSING--';
++          } else {
++              $dif = '';
++              if (!defined $dbsum4) {
++                  $dif .= ' -NO-MD4-';
++              } elsif ($sum4 ne $dbsum4) {
++                  $dif .= ' -MD4-CHANGED-';
++              }
++              if (!defined $dbsum5) {
++                  $dif .= ' ---NO-MD5---';
++              } elsif ($sum5 ne $dbsum5) {
++                  $dif .= ' -MD5-CHANGED-';
++              }
++              if ($dif eq '') {
++                  print " ====OK====\n" if $verbosity > 1;
++                  next;
++              }
++              $dif =~ s/MD4-CHANGED MD5-//;
++          }
++          if ($verbosity < 2) {
++              print $verbosity ? ' ' : "$reldir/";
++              print $fn;
++          }
++          print $dif, "\n";
++          $exit_code = 1;
++      } else {
++          print "\n" if $verbosity > 1;
++          $rep_sum_H->execute($disk_id, $ino, $size, $mtime, $ctime, 4, $sum4);
++          $rep_sum_H->execute($disk_id, $ino, $size, $mtime, $ctime, 5, $sum5);
++      }
++    }
++
++    closedir DP;
++
++    unshift(@dirs, sort @subdirs) if $recurse_opt;
++}
++
++exit $exit_code;
++
++sub usage
++{
++    die <<EOT;
++Usage: rsyncsums --db=CONFIG_FILE [OPTIONS] [DIRS]
++
++Options:
++     --db=FILE     Specify the config FILE to read for the DB info.
++ -m, --mounts      Update mount info.
++ -r, --recurse     Scan files in subdirectories too.
++ -c, --check       Check if the checksums are right (doesn't update).
++ -v, --verbose     Mention what we're doing.  Repeat for more info.
++ -h, --help        Display this help message.
++EOT
++}
index f9cdbe7..e8ea5a8 100644 (file)
@@ -34,7 +34,7 @@ diff --git a/generator.c b/generator.c
                diff = u_strcmp(fmid->basename, f->basename);
                if (diff == 0) {
                        good_match = mid;
-@@ -1968,6 +1970,21 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1967,6 +1969,21 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                fnamecmp = partialptr;
                fnamecmp_type = FNAMECMP_PARTIAL_DIR;
                statret = 0;
index 78b14a7..e28ed1d 100644 (file)
@@ -71,7 +71,7 @@ diff --git a/flist.c b/flist.c
  static char empty_sum[MAX_DIGEST_LEN];
  static int flist_count_offset; /* for --delete --progress */
  static int dir_count = 0;
-@@ -298,6 +301,45 @@ static int is_excluded(const char *fname, int is_dir, int filter_level)
+@@ -287,6 +290,45 @@ static int is_excluded(const char *fname, int is_dir, int filter_level)
        return 0;
  }
  
@@ -117,7 +117,7 @@ diff --git a/flist.c b/flist.c
  static void send_directory(int f, struct file_list *flist,
                           char *fbuf, int len, int flags);
  
-@@ -2251,6 +2293,25 @@ struct file_list *recv_file_list(int f)
+@@ -2259,6 +2301,25 @@ struct file_list *recv_file_list(int f)
  
        flist_sort_and_clean(flist, relative_paths);
  
@@ -480,9 +480,9 @@ diff --git a/generator.c b/generator.c
 +                                            delete_during < 0 ? DEL_NO_DELETIONS : 0);
 +                      } else
                                change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
                }
-@@ -1765,8 +1917,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+               goto cleanup;
+@@ -1764,8 +1916,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        goto cleanup;
                }
  #endif
@@ -498,7 +498,7 @@ diff --git a/generator.c b/generator.c
                rsyserr(FERROR_XFER, stat_errno, "recv_generator: failed to stat %s",
                        full_fname(fname));
                goto cleanup;
-@@ -2143,6 +2301,12 @@ void generate_files(int f_out, const char *local_name)
+@@ -2142,6 +2300,12 @@ void generate_files(int f_out, const char *local_name)
        if (verbose > 2)
                rprintf(FINFO, "generator starting pid=%ld\n", (long)getpid());
  
@@ -511,7 +511,7 @@ diff --git a/generator.c b/generator.c
        if (delete_before && !solo_file && cur_flist->used > 0)
                do_delete_pass();
        if (delete_during == 2) {
-@@ -2153,7 +2317,7 @@ void generate_files(int f_out, const char *local_name)
+@@ -2152,7 +2316,7 @@ void generate_files(int f_out, const char *local_name)
        }
        do_progress = 0;
  
@@ -520,7 +520,7 @@ diff --git a/generator.c b/generator.c
                whole_file = 0;
        if (verbose >= 2) {
                rprintf(FINFO, "delta-transmission %s\n",
-@@ -2192,7 +2356,7 @@ void generate_files(int f_out, const char *local_name)
+@@ -2191,7 +2355,7 @@ void generate_files(int f_out, const char *local_name)
                                                dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
                                        } else
                                                dirdev = MAKEDEV(0, 0);
@@ -529,7 +529,7 @@ diff --git a/generator.c b/generator.c
                                } else
                                        change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
                        }
-@@ -2236,7 +2400,21 @@ void generate_files(int f_out, const char *local_name)
+@@ -2235,7 +2399,21 @@ void generate_files(int f_out, const char *local_name)
        } while ((cur_flist = cur_flist->next) != NULL);
  
        if (delete_during)
@@ -641,7 +641,7 @@ diff --git a/rsync.yo b/rsync.yo
 diff --git a/util.c b/util.c
 --- a/util.c
 +++ b/util.c
-@@ -1112,6 +1112,32 @@ int handle_partial_dir(const char *fname, int create)
+@@ -1096,6 +1096,32 @@ int handle_partial_dir(const char *fname, int create)
        return 1;
  }
  
index 7b922a4..276c98e 100644 (file)
@@ -18,7 +18,7 @@ diff --git a/generator.c b/generator.c
  extern int ignore_existing;
  extern int ignore_non_existing;
  extern int inplace;
-@@ -1706,6 +1707,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1705,6 +1706,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                goto cleanup;
        }
  
@@ -32,7 +32,7 @@ diff --git a/generator.c b/generator.c
        fnamecmp = fname;
        fnamecmp_type = FNAMECMP_FNAME;
  
-@@ -2053,6 +2061,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
+@@ -2052,6 +2060,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
                        ignore_existing = -ignore_existing;
                        ignore_non_existing = -ignore_non_existing;
                        update_only = -update_only;
@@ -40,7 +40,7 @@ diff --git a/generator.c b/generator.c
                        always_checksum = -always_checksum;
                        size_only = -size_only;
                        append_mode = -append_mode;
-@@ -2078,6 +2087,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
+@@ -2077,6 +2086,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
                        ignore_existing = -ignore_existing;
                        ignore_non_existing = -ignore_non_existing;
                        update_only = -update_only;
index 80f0d91..ca7c1aa 100644 (file)
@@ -85,7 +85,7 @@ diff --git a/flist.c b/flist.c
  extern int uid_ndx;
  extern int gid_ndx;
  extern int eol_nulls;
-@@ -381,6 +382,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+@@ -386,6 +387,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
  {
        static time_t modtime;
        static mode_t mode;
@@ -95,7 +95,7 @@ diff --git a/flist.c b/flist.c
  #ifdef SUPPORT_HARD_LINKS
        static int64 dev;
  #endif
-@@ -410,6 +414,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
+@@ -415,6 +419,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                xflags |= XMIT_SAME_MODE;
        else
                mode = file->mode;
@@ -153,7 +153,7 @@ diff --git a/flist.c b/flist.c
        if (preserve_uid)
                F_OWNER(file) = uid;
        if (preserve_gid) {
-@@ -1211,6 +1238,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1222,6 +1249,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                OPT_EXTRA(file, 0)->unum = (uint32)(st.st_size >> 32);
        }
        file->mode = st.st_mode;
@@ -267,7 +267,7 @@ diff --git a/generator.c b/generator.c
                                rsyserr(FERROR_XFER, errno,
                                        "failed to modify permissions on %s",
                                        full_fname(fname));
-@@ -1500,6 +1532,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1499,6 +1531,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
                                       exists);
        }
@@ -278,7 +278,7 @@ diff --git a/generator.c b/generator.c
  
  #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
-@@ -2012,13 +2048,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
+@@ -2011,13 +2047,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
                        continue;
                fname = f_name(file, NULL);
                if (!(file->mode & S_IWUSR))
@@ -566,7 +566,7 @@ diff --git a/rsync.h b/rsync.h
  
  /* These flags are used in the live flist data. */
  
-@@ -151,6 +152,7 @@
+@@ -152,6 +153,7 @@
  
  #define ATTRS_REPORT          (1<<0)
  #define ATTRS_SKIP_MTIME      (1<<1)
@@ -574,7 +574,7 @@ diff --git a/rsync.h b/rsync.h
  
  #define FULL_FLUSH    1
  #define NORMAL_FLUSH  0
-@@ -177,6 +179,7 @@
+@@ -178,6 +180,7 @@
  #define ITEM_REPORT_GROUP (1<<6)
  #define ITEM_REPORT_ACL (1<<7)
  #define ITEM_REPORT_XATTR (1<<8)
@@ -582,7 +582,7 @@ diff --git a/rsync.h b/rsync.h
  #define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
  #define ITEM_XNAME_FOLLOWS (1<<12)
  #define ITEM_IS_NEW (1<<13)
-@@ -454,6 +457,28 @@ typedef unsigned int size_t;
+@@ -458,6 +461,28 @@ typedef unsigned int size_t;
  #endif
  #endif
  
@@ -611,7 +611,7 @@ diff --git a/rsync.h b/rsync.h
  /* Find a variable that is either exactly 32-bits or longer.
   * If some code depends on 32-bit truncation, it will need to
   * take special action in a "#if SIZEOF_INT32 > 4" section. */
-@@ -622,6 +647,7 @@ extern int file_extra_cnt;
+@@ -626,6 +651,7 @@ extern int file_extra_cnt;
  extern int inc_recurse;
  extern int uid_ndx;
  extern int gid_ndx;
@@ -619,7 +619,7 @@ diff --git a/rsync.h b/rsync.h
  extern int acls_ndx;
  extern int xattrs_ndx;
  
-@@ -659,6 +685,11 @@ extern int xattrs_ndx;
+@@ -663,6 +689,11 @@ extern int xattrs_ndx;
  /* When the associated option is on, all entries will have these present: */
  #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
  #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
index 39437a5..169bf91 100644 (file)
@@ -55,7 +55,7 @@ diff --git a/flist.c b/flist.c
  extern int ignore_errors;
  extern int numeric_ids;
  extern int recurse;
-@@ -2688,6 +2689,7 @@ int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
+@@ -2696,6 +2697,7 @@ int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
  {
        int dif;
        const uchar *c1, *c2;
@@ -63,7 +63,7 @@ diff --git a/flist.c b/flist.c
        enum fnc_state state1, state2;
        enum fnc_type type1, type2;
        enum fnc_type t_path = protocol_version >= 29 ? t_PATH : t_ITEM;
-@@ -2798,7 +2800,15 @@ int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
+@@ -2806,7 +2808,15 @@ int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2)
                        if (type1 != type2)
                                return type1 == t_PATH ? 1 : -1;
                }
index 6c6ad1b..96bea34 100644 (file)
@@ -561,7 +561,7 @@ diff --git a/rsync.c b/rsync.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -820,6 +820,14 @@ struct stats {
+@@ -824,6 +824,14 @@ struct stats {
        int num_transferred_files;
  };
  
index c1ad7f6..51543c3 100644 (file)
@@ -435,7 +435,7 @@ diff --git a/rsync.h b/rsync.h
  
  #define SYMLINK_PREFIX "/rsyncd-munged/"
  #define SYMLINK_PREFIX_LEN ((int)sizeof SYMLINK_PREFIX - 1)
-@@ -539,6 +540,11 @@ typedef unsigned int size_t;
+@@ -543,6 +544,11 @@ typedef unsigned int size_t;
  # define SIZEOF_INT64 SIZEOF_OFF_T
  #endif
  
index d910772..a2b3868 100644 (file)
@@ -203,7 +203,7 @@ diff --git a/receiver.c b/receiver.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -604,6 +604,13 @@ struct ht_int64_node {
+@@ -608,6 +608,13 @@ struct ht_int64_node {
  #define ACLS_NEED_MASK 1
  #endif
  
index 16975c0..045d314 100644 (file)
@@ -18,9 +18,9 @@ diff --git a/options.c b/options.c
  int basis_dir_cnt = 0;
  char *dest_option = NULL;
  
-+#define MAX_REMOTE_ARGS (MAX_SERVER_ARGS/2)
++static int remote_option_alloc = 0;
 +int remote_option_cnt = 0;
-+const char *remote_options[MAX_SERVER_ARGS+1] = { "ARG0" };
++const char **remote_options = NULL;
 +
  int verbose = 0;
  int quiet = 0;
@@ -41,7 +41,7 @@ diff --git a/options.c b/options.c
    {"protocol",         0,  POPT_ARG_INT,    &protocol_version, 0, 0, 0 },
    {"checksum-seed",    0,  POPT_ARG_INT,    &checksum_seed, 0, 0, 0 },
    {"server",           0,  POPT_ARG_NONE,   0, OPT_SERVER, 0, 0 },
-@@ -1140,6 +1146,20 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -1140,6 +1146,26 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                        }
                        break;
  
@@ -52,17 +52,23 @@ diff --git a/options.c b/options.c
 +                                      "Remote option must start with a dash: %s\n", arg);
 +                              return 0;
 +                      }
-+                      if (remote_option_cnt >= MAX_REMOTE_ARGS) {
-+                              rprintf(FERROR, "too many remote options specified.\n");
-+                              exit_cleanup(RERR_SYNTAX);
++                      if (remote_option_cnt+3 > remote_option_alloc) {
++                              remote_option_alloc += 16;
++                              remote_options = realloc_array(remote_options,
++                                                      const char *, remote_option_alloc);
++                              if (!remote_options)
++                                      out_of_memory("parse_arguments");
++                              if (!remote_option_cnt)
++                                      remote_options[0] = "ARG0";
 +                      }
 +                      remote_options[++remote_option_cnt] = arg;
++                      remote_options[remote_option_cnt+1] = NULL;
 +                      break;
 +
                case OPT_WRITE_BATCH:
                        /* batch_name is already set */
                        write_batch = 1;
-@@ -1826,6 +1846,11 @@ void server_options(char **args, int *argc_p)
+@@ -1826,6 +1852,11 @@ void server_options(char **args, int *argc_p)
  #endif
        argstr[x] = '\0';
  
@@ -74,7 +80,7 @@ diff --git a/options.c b/options.c
        args[ac++] = argstr;
  
  #ifdef ICONV_OPTION
-@@ -2048,6 +2073,21 @@ void server_options(char **args, int *argc_p)
+@@ -2048,6 +2079,21 @@ void server_options(char **args, int *argc_p)
        else if (remove_source_files)
                args[ac++] = "--remove-sent-files";
  
@@ -99,29 +105,19 @@ diff --git a/options.c b/options.c
 diff --git a/pipe.c b/pipe.c
 --- a/pipe.c
 +++ b/pipe.c
-@@ -22,12 +22,15 @@
- #include "rsync.h"
-+extern int am_root;
- extern int am_sender;
- extern int am_server;
- extern int blocking_io;
+@@ -28,6 +28,8 @@ extern int blocking_io;
  extern int filesfrom_fd;
  extern mode_t orig_umask;
  extern char *logfile_name;
 +extern int remote_option_cnt;
-+extern const char *remote_options[];
++extern const char **remote_options;
  extern struct chmod_mode_struct *chmod_modes;
  
  /**
-@@ -139,6 +142,18 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
+@@ -139,6 +141,15 @@ pid_t local_child(int argc, char **argv, int *f_in, int *f_out,
                        logfile_close();
                }
  
-+              if (am_root < 0)
-+                      am_root = 0;
-+
 +              if (remote_option_cnt) {
 +                      int rc = remote_option_cnt + 1;
 +                      const char **rv = remote_options;
@@ -145,7 +141,7 @@ diff --git a/rsync.yo b/rsync.yo
       --out-format=FORMAT     output updates using the specified FORMAT
       --log-file=FILE         log what we're doing to the specified FILE
       --log-file-format=FMT   log updates using the specified FMT
-@@ -1020,16 +1021,13 @@ This is a good way to backup data without using a super-user, and to store
+@@ -1020,16 +1021,16 @@ This is a good way to backup data without using a super-user, and to store
  ACLs from incompatible systems.
  
  The bf(--fake-super) option only affects the side where the option is used.
@@ -162,12 +158,15 @@ diff --git a/rsync.yo b/rsync.yo
 -"localhost" if you need to avoid this, possibly using the "lsh" shell
 -script (from the support directory) as a substitute for an actual remote
 -shell (see bf(--rsh)).
-+For a local copy, this option affects only the source.  Specify a
-+bf(--remote-option) to affect the destination.
++For a local copy, this option affects both the source and the destination.
++If you wish a local copy to enable this option just for the destination
++files, specify bf(-M--fake-super).  If you wish a local copy to enable
++this option just for the source files, combine bf(--fake-super) with
++bf(-M--super).
  
  This option is overridden by both bf(--super) and bf(--no-super).
  
-@@ -1275,6 +1273,36 @@ machine for use with the bf(--relative) option.  For instance:
+@@ -1275,6 +1276,36 @@ machine for use with the bf(--relative) option.  For instance:
  
  quote(tt(    rsync -avR --rsync-path="cd /a/b && rsync" host:c/d /e/))
  
@@ -189,7 +188,7 @@ diff --git a/rsync.yo b/rsync.yo
 +and that will make it fail in a cryptic fashion.
 +
 +Note that it is best to use a separate bf(--remote-option) for each option you
-+want to pass.  This makes your useage compatible with the bf(--preserve-spaces)
++want to pass.  This makes your useage compatible with the bf(--protect-args)
 +option.  If that option is off, any spaces in your remote options will be split
 +by the remote shell unless you take steps to protect them.
 +
@@ -204,7 +203,7 @@ diff --git a/rsync.yo b/rsync.yo
  dit(bf(-C, --cvs-exclude)) This is a useful shorthand for excluding a
  broad range of files that you often don't want to transfer between
  systems. It uses a similar algorithm to CVS to determine if
-@@ -1746,7 +1774,7 @@ option if you wish to override this.
+@@ -1746,7 +1777,7 @@ option if you wish to override this.
  Here's a example command that requests the remote side to log what is
  happening:
  
@@ -213,27 +212,3 @@ diff --git a/rsync.yo b/rsync.yo
  
  This is very useful if you need to debug why a connection is closing
  unexpectedly.
-diff --git a/testsuite/chown.test b/testsuite/chown.test
---- a/testsuite/chown.test
-+++ b/testsuite/chown.test
-@@ -16,7 +16,7 @@
- case $0 in
- *fake*)
-     $RSYNC --version | grep ", xattrs" >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
--    RSYNC="$RSYNC --fake-super"
-+    RSYNC="$RSYNC --fake-super -M--fake-super"
-     TLS_ARGS=--fake-super
-     case "`xattr 2>&1`" in
-     *--list:*)
-diff --git a/testsuite/devices.test b/testsuite/devices.test
---- a/testsuite/devices.test
-+++ b/testsuite/devices.test
-@@ -17,7 +17,7 @@ outfile="$scratchdir/rsync.out"
- case $0 in
- *fake*)
-     $RSYNC --version | grep ", xattrs" >/dev/null || test_skipped "Rsync needs xattrs for fake device tests"
--    RSYNC="$RSYNC --fake-super"
-+    RSYNC="$RSYNC --fake-super -M--fake-super"
-     TLS_ARGS=--fake-super
-     case "`xattr 2>&1`" in
-     *--list:*)
index 6265396..1968278 100644 (file)
@@ -25,7 +25,7 @@ diff --git a/flist.c b/flist.c
  extern struct stats stats;
  extern char *filesfrom_host;
  
-@@ -1542,6 +1543,9 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
+@@ -1553,6 +1554,9 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
                }
  
                send_file_name(f, flist, fbuf, NULL, flags, filter_level);
index 9832370..8d0815c 100644 (file)
--- a/slp.diff
+++ b/slp.diff
@@ -180,7 +180,7 @@ diff --git a/options.c b/options.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -190,6 +190,10 @@
+@@ -191,6 +191,10 @@
  #define SIGNIFICANT_ITEM_FLAGS (~(\
        ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
  
index e045b8e..cfd5dbc 100644 (file)
@@ -314,7 +314,7 @@ diff --git a/receiver.c b/receiver.c
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -134,6 +134,7 @@
+@@ -135,6 +135,7 @@
  #define IOERR_DEL_LIMIT (1<<2)
  
  #define MAX_ARGS 1000
index 1ce8ab9..87e7a66 100644 (file)
@@ -45,6 +45,6 @@ diff --git a/options.c b/options.c
 +                      setvbuf(stdout, NULL, _IOLBF, 0);
 +      }
 +
-       if (human_readable && argc == 2) {
+       if (human_readable && argc == 2 && !am_server) {
                /* Allow the old meaning of 'h' (--help) on its own. */
                usage(FINFO);
index 425e288..f09797a 100644 (file)
@@ -36,7 +36,7 @@ diff --git a/flist.c b/flist.c
                                gid = match_gid(gid, &gid_flags);
                }
        }
-@@ -2158,8 +2159,13 @@ struct file_list *recv_file_list(int f)
+@@ -2166,8 +2167,13 @@ struct file_list *recv_file_list(int f)
        int dstart, flags;
        int64 start_read;