The patches for 3.2.7pre1.
[rsync-patches.git] / checksum-reading.diff
index 49e6957a154159cfb7939b470ff52589da416e36..5c1466a370e5b8df85e5371900c510c755b6b42f 100644 (file)
@@ -16,23 +16,11 @@ To use this patch, run these commands for a successful build:
     ./configure                               (optional if already run)
     make
 
-based-on: 63f91976112b8b2118cc17eb5fc8142175566f4f
-diff --git a/checksum.c b/checksum.c
---- a/checksum.c
-+++ b/checksum.c
-@@ -98,7 +98,7 @@ 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, OFF_T size, char *sum)
- {
-       struct map_struct *buf;
-       OFF_T i, len = size;
+based-on: ed4b3448be243b1bdb30a5da811f1e217f5a0601
 diff --git a/clientserver.c b/clientserver.c
 --- a/clientserver.c
 +++ b/clientserver.c
-@@ -42,6 +42,8 @@ extern int numeric_ids;
+@@ -44,6 +44,8 @@ extern int numeric_ids;
  extern int filesfrom_fd;
  extern int remote_protocol;
  extern int protocol_version;
@@ -41,7 +29,7 @@ diff --git a/clientserver.c b/clientserver.c
  extern int io_timeout;
  extern int no_detach;
  extern int write_batch;
-@@ -908,6 +910,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
+@@ -1106,6 +1108,9 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
        } else if (am_root < 0) /* Treat --fake-super from client as --super. */
                am_root = 2;
  
@@ -51,6 +39,17 @@ diff --git a/clientserver.c b/clientserver.c
        if (filesfrom_fd == 0)
                filesfrom_fd = f_in;
  
+diff --git a/daemon-parm.txt b/daemon-parm.txt
+--- a/daemon-parm.txt
++++ b/daemon-parm.txt
+@@ -49,6 +49,7 @@ INTEGER      max_connections         0
+ INTEGER       max_verbosity           1
+ INTEGER       timeout                 0
++ENUM  checksum_files          CSF_IGNORE_FILES
+ ENUM  syslog_facility         LOG_DAEMON
+ BOOL  fake_super              False
 diff --git a/flist.c b/flist.c
 --- a/flist.c
 +++ b/flist.c
@@ -70,7 +69,7 @@ diff --git a/flist.c b/flist.c
  extern int module_id;
  extern int ignore_errors;
  extern int numeric_ids;
-@@ -58,6 +60,7 @@ extern int implied_dirs;
+@@ -62,6 +64,7 @@ extern int implied_dirs;
  extern int ignore_perishable;
  extern int non_perishable_cnt;
  extern int prune_empty_dirs;
@@ -78,15 +77,15 @@ diff --git a/flist.c b/flist.c
  extern int copy_links;
  extern int copy_unsafe_links;
  extern int protocol_version;
-@@ -69,6 +72,7 @@ extern int sender_symlink_iconv;
- extern int output_needs_newline;
+@@ -74,6 +77,7 @@ extern int output_needs_newline;
  extern int sender_keeps_checksum;
+ extern int trust_sender_filter;
  extern int unsort_ndx;
 +extern char *basis_dir[];
  extern uid_t our_uid;
  extern struct stats stats;
  extern char *filesfrom_host;
-@@ -86,6 +90,20 @@ extern int filesfrom_convert;
+@@ -92,6 +96,20 @@ extern int filesfrom_convert;
  extern iconv_t ic_send, ic_recv;
  #endif
  
@@ -107,20 +106,21 @@ diff --git a/flist.c b/flist.c
  #define PTR_SIZE (sizeof (struct file_struct *))
  
  int io_error;
-@@ -128,7 +146,11 @@ static char tmp_sum[MAX_DIGEST_LEN];
- static char empty_sum[MAX_DIGEST_LEN];
+@@ -136,8 +154,12 @@ static char empty_sum[MAX_DIGEST_LEN];
  static int flist_count_offset; /* for --delete --progress */
+ static int show_filelist_progress;
  
--static void flist_sort_and_clean(struct file_list *flist, int strip_root);
 +static struct csum_cache {
 +      struct file_list *flist;
 +} *csum_cache = NULL;
 +
+ static struct file_list *flist_new(int flags, const char *msg);
+-static void flist_sort_and_clean(struct file_list *flist, int strip_root);
 +static void flist_sort_and_clean(struct file_list *flist, int flags);
  static void output_flist(struct file_list *flist);
  
  void init_flist(void)
-@@ -343,6 +365,238 @@ static void flist_done_allocating(struct file_list *flist)
+@@ -329,6 +351,235 @@ static void flist_done_allocating(struct file_list *flist)
                flist->pool_boundary = ptr;
  }
  
@@ -128,11 +128,8 @@ diff --git a/flist.c b/flist.c
 +{
 +      int slot, slots = am_sender ? 1 : basis_dir_cnt + 1;
 +
-+      if (!csum_cache) {
++      if (!csum_cache)
 +              csum_cache = new_array0(struct csum_cache, slots);
-+              if (!csum_cache)
-+                      out_of_memory("reset_checksum_cache");
-+      }
 +
 +      for (slot = 0; slot < slots; slot++) {
 +              struct file_list *flist = csum_cache[slot].flist;
@@ -193,7 +190,7 @@ diff --git a/flist.c b/flist.c
 +      F_CTIME(file) = ctime;
 +      F_INODE(file) = inode;
 +      bp = F_SUM(file);
-+      memcpy(bp, sum, checksum_len);
++      memcpy(bp, sum, flist_csum_len);
 +
 +      flist_expand(flist, 1);
 +      flist->files[flist->used++] = file;
@@ -233,12 +230,12 @@ diff --git a/flist.c b/flist.c
 +
 +      while (fgets(line, sizeof line, fp)) {
 +              cp = line;
-+              if (protocol_version >= 30) {
++              if (file_sum_nni->num == CSUM_MD5) {
 +                      char *alt_sum = cp;
 +                      if (*cp == '=')
 +                              while (*++cp == '=') {}
 +                      else
-+                              while (isXDigit(cp)) cp++;
++                              while (isHexDigit(cp)) cp++;
 +                      if (cp - alt_sum != MD4_DIGEST_LEN*2 || *cp != ' ')
 +                              break;
 +                      while (*++cp == ' ') {}
@@ -247,9 +244,9 @@ diff --git a/flist.c b/flist.c
 +              if (*cp == '=') {
 +                      continue;
 +              } else {
-+                      for (i = 0; i < checksum_len*2; i++, cp++) {
++                      for (i = 0; i < flist_csum_len*2; i++, cp++) {
 +                              int x;
-+                              if (isXDigit(cp)) {
++                              if (isHexDigit(cp)) {
 +                                      if (isDigit(cp))
 +                                              x = *cp - '0';
 +                                      else
@@ -268,12 +265,12 @@ diff --git a/flist.c b/flist.c
 +                      break;
 +              while (*++cp == ' ') {}
 +
-+              if (protocol_version < 30) {
++              if (file_sum_nni->num < CSUM_MD5) {
 +                      char *alt_sum = cp;
 +                      if (*cp == '=')
 +                              while (*++cp == '=') {}
 +                      else
-+                              while (isXDigit(cp)) cp++;
++                              while (isHexDigit(cp)) cp++;
 +                      if (cp - alt_sum != MD5_DIGEST_LEN*2 || *cp != ' ')
 +                              break;
 +                      while (*++cp == ' ') {}
@@ -353,13 +350,13 @@ diff --git a/flist.c b/flist.c
 +              }
 +      }
 +
-+      file_checksum(fname, stp->st_size, sum_buf);
++      file_checksum(fname, stp, sum_buf);
 +}
 +
  /* 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
-@@ -1136,7 +1390,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1231,7 +1482,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                              STRUCT_STAT *stp, int flags, int filter_level)
  {
        static char *lastdir;
@@ -368,7 +365,7 @@ diff --git a/flist.c b/flist.c
        struct file_struct *file;
        char thisname[MAXPATHLEN];
        char linkname[MAXPATHLEN];
-@@ -1282,9 +1536,16 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1377,9 +1628,16 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                        memcpy(lastdir, thisname, len);
                        lastdir[len] = '\0';
                        lastdir_len = len;
@@ -386,12 +383,12 @@ diff --git a/flist.c b/flist.c
        basename_len = strlen(basename) + 1; /* count the '\0' */
  
  #ifdef SUPPORT_LINKS
-@@ -1302,11 +1563,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1409,11 +1667,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                extra_len += EXTRA_LEN;
  #endif
  
 -      if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
--              file_checksum(thisname, tmp_sum, st.st_size);
+-              file_checksum(thisname, &st, tmp_sum);
 -              if (sender_keeps_checksum)
 -                      extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
 -      }
@@ -400,33 +397,33 @@ diff --git a/flist.c b/flist.c
  
  #if EXTRA_ROUNDING > 0
        if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
-@@ -1391,8 +1649,14 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1502,8 +1757,14 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                return NULL;
        }
  
 -      if (sender_keeps_checksum && S_ISREG(st.st_mode))
--              memcpy(F_SUM(file), tmp_sum, checksum_len);
+-              memcpy(F_SUM(file), tmp_sum, flist_csum_len);
 +      if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
 +              if (flist && checksum_files)
 +                      get_cached_checksum(0, thisname, file, &st, tmp_sum);
 +              else
-+                      file_checksum(thisname, st.st_size, tmp_sum);
++                      file_checksum(thisname, &st, tmp_sum);
 +              if (sender_keeps_checksum)
-+                      memcpy(F_SUM(file), tmp_sum, checksum_len);
++                      memcpy(F_SUM(file), tmp_sum, flist_csum_len);
 +      }
  
        if (unsort_ndx)
                F_NDX(file) = stats.num_dirs;
-@@ -2553,7 +2817,7 @@ struct file_list *recv_file_list(int f)
-                       rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
-       }
+@@ -2720,7 +2981,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
+       /* The --relative option sends paths with a leading slash, so we need
+        * to specify the strip_root option here.  We rejected leading slashes
+        * for a non-relative transfer in recv_file_entry(). */
 -      flist_sort_and_clean(flist, relative_paths);
 +      flist_sort_and_clean(flist, relative_paths ? CLEAN_STRIP_ROOT : 0);
  
        if (protocol_version < 30) {
                /* Recv the io_error flag */
-@@ -2776,7 +3040,7 @@ void flist_free(struct file_list *flist)
+@@ -2965,7 +3226,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. */
@@ -435,7 +432,7 @@ diff --git a/flist.c b/flist.c
  {
        char fbuf[MAXPATHLEN];
        int i, prev_i;
-@@ -2827,7 +3091,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+@@ -3016,7 +3277,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. */
@@ -444,7 +441,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;
-@@ -2843,8 +3107,8 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+@@ -3032,8 +3293,8 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
                        } else
                                keep = j, drop = i;
  
@@ -455,7 +452,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);
-@@ -2866,7 +3130,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
+@@ -3055,7 +3316,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root)
        }
        flist->high = prev_i;
  
@@ -467,7 +464,7 @@ diff --git a/flist.c b/flist.c
 diff --git a/generator.c b/generator.c
 --- a/generator.c
 +++ b/generator.c
-@@ -51,6 +51,7 @@ extern int delete_after;
+@@ -54,6 +54,7 @@ extern int delete_after;
  extern int missing_args;
  extern int msgdone_cnt;
  extern int ignore_errors;
@@ -475,37 +472,46 @@ diff --git a/generator.c b/generator.c
  extern int remove_source_files;
  extern int delay_updates;
  extern int update_only;
-@@ -565,7 +566,7 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
+@@ -614,7 +615,7 @@ static enum filetype get_file_type(mode_t mode)
+ }
  
  /* Perform our quick-check heuristic for determining if a file is unchanged. */
--int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
-+int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st, int slot)
+-int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file, STRUCT_STAT *st)
++int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file, STRUCT_STAT *st, int slot)
  {
-       if (st->st_size != F_LENGTH(file))
-               return 0;
-@@ -574,7 +575,10 @@ 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 (checksum_files && slot >= 0)
-+                      get_cached_checksum(slot, fn, file, st, sum);
-+              else
-+                      file_checksum(fn, st->st_size, sum);
-               return memcmp(sum, F_SUM(file), checksum_len) == 0;
-       }
+       switch (ftype) {
+         case FT_REG:
+@@ -625,7 +626,10 @@ int quick_check_ok(enum filetype ftype, const char *fn, struct file_struct *file
+                * of the file mtime to determine whether to sync. */
+               if (always_checksum > 0) {
+                       char sum[MAX_DIGEST_LEN];
+-                      file_checksum(fn, st, sum);
++                      if (checksum_files && slot >= 0)
++                              get_cached_checksum(slot, fn, file, st, sum);
++                      else
++                              file_checksum(fn, st, sum);
+                       return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
+               }
  
-@@ -867,7 +871,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
+@@ -956,7 +960,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
+                       best_match = j;
+                       match_level = 1;
+               }
+-              if (!quick_check_ok(FT_REG, cmpbuf, file, &sxp->st))
++              if (!quick_check_ok(FT_REG, cmpbuf, file, &sxp->st, j+1))
+                       continue;
+               if (match_level == 1) {
+                       best_match = j;
+@@ -1079,7 +1083,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
                        match_level = 1;
-                       /* FALL THROUGH */
-               case 1:
--                      if (!unchanged_file(cmpbuf, file, &sxp->st))
-+                      if (!unchanged_file(cmpbuf, file, &sxp->st, j+1))
-                               continue;
                        best_match = j;
+               }
+-              if (!quick_check_ok(ftype, cmpbuf, file, &sxp->st))
++              if (!quick_check_ok(ftype, cmpbuf, file, &sxp->st, j+1))
+                       continue;
+               if (match_level < 2) {
                        match_level = 2;
-@@ -1173,7 +1177,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1215,7 +1219,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
         * --ignore-non-existing, daemon exclude, or mkdir failure. */
        static struct file_struct *skip_dir = NULL;
        static struct file_list *fuzzy_dirlist[MAX_BASIS_DIRS+1];
@@ -514,7 +520,7 @@ diff --git a/generator.c b/generator.c
        struct file_struct *fuzzy_file = NULL;
        int fd = -1, f_copy = -1;
        stat_x sx, real_sx;
-@@ -1264,8 +1268,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1332,8 +1336,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                                fuzzy_dirlist[i] = NULL;
                                        }
                                }
@@ -526,117 +532,123 @@ diff --git a/generator.c b/generator.c
  #ifdef SUPPORT_ACLS
                        if (!preserve_perms)
                                dflt_perms = default_perms_for_dir(dn);
-@@ -1273,7 +1278,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1341,6 +1346,24 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                }
                parent_dirname = dn;
  
--              if (need_fuzzy_dirlist && S_ISREG(file->mode)) {
-+              if (need_new_dirscan && S_ISREG(file->mode)) {
-                       int i;
-                       strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
-                       for (i = 0; i < fuzzy_basis; i++) {
-@@ -1285,7 +1290,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
-                                       fuzzy_dirlist[i] = NULL;
-                               }
-                       }
--                      need_fuzzy_dirlist = 0;
++              if (need_new_dirscan && ftype == FT_REG) {
++                      int i;
++                      strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
++                      for (i = 0; i < fuzzy_basis; i++) {
++                              if (i && pathjoin(fnamecmpbuf, MAXPATHLEN, basis_dir[i-1], dn) >= MAXPATHLEN)
++                                      continue;
++                              fuzzy_dirlist[i] = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES | GDL_PERHAPS_DIR);
++                              if (fuzzy_dirlist[i] && fuzzy_dirlist[i]->used == 0) {
++                                      flist_free(fuzzy_dirlist[i]);
++                                      fuzzy_dirlist[i] = NULL;
++                              }
++                      }
 +                      if (checksum_files) {
 +                              reset_checksum_cache();
 +                      }
 +                      need_new_dirscan = 0;
++              }
++
+               statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
+               stat_errno = errno;
+       }
+@@ -1387,7 +1410,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+                       if (INFO_GTE(SKIP, 2)) {
+                               if (ftype != stype)
+                                       suf = " (type change)";
+-                              else if (!quick_check_ok(ftype, fname, file, &sx.st))
++                              else if (!quick_check_ok(ftype, fname, file, &sx.st, 0))
+                                       suf = always_checksum ? " (sum change)" : " (file change)";
+                               else if (!unchanged_attrs(fname, file, &sx))
+                                       suf = " (attr change)";
+@@ -1558,7 +1581,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+                       goto cleanup;
                }
+               if (statret == 0) {
+-                      if (stype == FT_SYMLINK && quick_check_ok(stype, fname, file, &sx.st)) {
++                      if (stype == FT_SYMLINK && quick_check_ok(stype, fname, file, &sx.st, 0)) {
+                               /* The link is pointing to the right place. */
+                               set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+                               if (itemizing)
+@@ -1627,7 +1650,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+               if (statret == 0) {
+                       if (ftype != stype)
+                               statret = -1;
+-                      else if (quick_check_ok(ftype, fname, file, &sx.st)) {
++                      else if (quick_check_ok(ftype, fname, file, &sx.st, 0)) {
+                               /* The device or special file is identical. */
+                               set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+                               if (itemizing)
+@@ -1752,22 +1775,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+               partialptr = NULL;
  
-               statret = link_stat(fname, &sx.st, keep_dirlinks && is_dir);
-@@ -1724,7 +1732,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+       if (statret != 0 && fuzzy_basis) {
+-              if (need_fuzzy_dirlist) {
+-                      const char *dn = file->dirname ? file->dirname : ".";
+-                      int i;
+-                      strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf);
+-                      for (i = 0; i < fuzzy_basis; i++) {
+-                              if (i && pathjoin(fnamecmpbuf, MAXPATHLEN, basis_dir[i-1], dn) >= MAXPATHLEN)
+-                                      continue;
+-                              fuzzy_dirlist[i] = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES | GDL_PERHAPS_DIR);
+-                              if (fuzzy_dirlist[i] && fuzzy_dirlist[i]->used == 0) {
+-                                      flist_free(fuzzy_dirlist[i]);
+-                                      fuzzy_dirlist[i] = NULL;
+-                              }
+-                      }
+-                      need_fuzzy_dirlist = 0;
+-              }
+-
+               /* Sets fnamecmp_type to FNAMECMP_FUZZY or above. */
+               fuzzy_file = find_fuzzy(file, fuzzy_dirlist, &fnamecmp_type);
+               if (fuzzy_file) {
+@@ -1806,7 +1813,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                ;
-       else if (fnamecmp_type == FNAMECMP_FUZZY)
+       else if (fnamecmp_type >= FNAMECMP_FUZZY)
                ;
--      else if (unchanged_file(fnamecmp, file, &sx.st)) {
-+      else if (unchanged_file(fnamecmp, file, &sx.st, fnamecmp_type == FNAMECMP_FNAME ? 0 : -1)) {
+-      else if (quick_check_ok(FT_REG, fnamecmp, file, &sx.st)) {
++      else if (quick_check_ok(FT_REG, fnamecmp, file, &sx.st, fnamecmp_type == FNAMECMP_FNAME ? 0 : -1)) {
                if (partialptr) {
                        do_unlink(partialptr);
                        handle_partial_dir(partialptr, PDIR_DELETE);
 diff --git a/hlink.c b/hlink.c
 --- a/hlink.c
 +++ b/hlink.c
-@@ -410,7 +410,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
+@@ -406,7 +406,7 @@ int hard_link_check(struct file_struct *file, int ndx, char *fname,
                                }
                                break;
                        }
--                      if (!unchanged_file(cmpbuf, file, &alt_sx.st))
-+                      if (!unchanged_file(cmpbuf, file, &alt_sx.st, j+1))
+-                      if (!quick_check_ok(FT_REG, cmpbuf, file, &alt_sx.st))
++                      if (!quick_check_ok(FT_REG, cmpbuf, file, &alt_sx.st, j+1))
                                continue;
                        statret = 1;
                        if (unchanged_attrs(cmpbuf, file, &alt_sx))
-diff --git a/itypes.h b/itypes.h
---- a/itypes.h
-+++ b/itypes.h
-@@ -23,6 +23,12 @@ isDigit(const char *ptr)
- }
- static inline int
-+isXDigit(const char *ptr)
-+{
-+      return isxdigit(*(unsigned char *)ptr);
-+}
-+
-+static inline int
- isPrint(const char *ptr)
- {
-       return isprint(*(unsigned char *)ptr);
 diff --git a/loadparm.c b/loadparm.c
 --- a/loadparm.c
 +++ b/loadparm.c
-@@ -134,6 +134,7 @@ typedef struct {
- /* NOTE: update this macro if the last char* variable changes! */
- #define LOCAL_STRING_COUNT() (offsetof(local_vars, uid) / sizeof (char*) + 1)
-+      int checksum_files;
-       int max_connections;
-       int max_verbosity;
-       int syslog_facility;
-@@ -208,6 +209,7 @@ static const all_vars Defaults = {
-  /* temp_dir; */              NULL,
-  /* uid; */                   NULL,
-+ /* checksum_files; */                CSF_IGNORE_FILES,
-  /* max_connections; */               0,
-  /* max_verbosity; */         1,
-  /* syslog_facility; */               LOG_DAEMON,
-@@ -310,6 +312,13 @@ static struct enum_list enum_facilities[] = {
+@@ -162,6 +162,13 @@ static struct enum_list enum_syslog_facility[] = {
        { -1, NULL }
  };
  
-+static struct enum_list enum_csum_modes[] = {
++static struct enum_list enum_checksum_files[] = {
 +      { CSF_IGNORE_FILES, "none" },
 +      { CSF_LAX_MODE, "lax" },
 +      { CSF_STRICT_MODE, "strict" },
 +      { -1, NULL }
 +};
 +
- static struct parm_struct parm_table[] =
- {
-  {"address",           P_STRING, P_GLOBAL,&Vars.g.bind_address,        NULL,0},
-@@ -321,6 +330,7 @@ static struct parm_struct parm_table[] =
-  {"auth users",        P_STRING, P_LOCAL, &Vars.l.auth_users,          NULL,0},
-  {"charset",           P_STRING, P_LOCAL, &Vars.l.charset,             NULL,0},
-+ {"checksum files",    P_ENUM,   P_LOCAL, &Vars.l.checksum_files,      enum_csum_modes,0},
-  {"comment",           P_STRING, P_LOCAL, &Vars.l.comment,             NULL,0},
-  {"dont compress",     P_STRING, P_LOCAL, &Vars.l.dont_compress,       NULL,0},
-  {"exclude from",      P_STRING, P_LOCAL, &Vars.l.exclude_from,        NULL,0},
-@@ -477,6 +487,7 @@ FN_LOCAL_STRING(lp_secrets_file, secrets_file)
- FN_LOCAL_STRING(lp_temp_dir, temp_dir)
- FN_LOCAL_STRING(lp_uid, uid)
-+FN_LOCAL_INTEGER(lp_checksum_files, checksum_files)
- FN_LOCAL_INTEGER(lp_max_connections, max_connections)
- FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
- FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
+ /* Expand %VAR% references.  Any unknown vars or unrecognized
+  * syntax leaves the raw chars unchanged. */
+ static char *expand_vars(const char *str)
 diff --git a/options.c b/options.c
 --- a/options.c
 +++ b/options.c
-@@ -113,6 +113,7 @@ size_t bwlimit_writemax = 0;
+@@ -126,6 +126,7 @@ size_t bwlimit_writemax = 0;
  int ignore_existing = 0;
  int ignore_non_existing = 0;
  int need_messages_from_generator = 0;
@@ -644,32 +656,24 @@ diff --git a/options.c b/options.c
  int max_delete = INT_MIN;
  OFF_T max_size = -1;
  OFF_T min_size = -1;
-@@ -671,6 +672,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,"     --sumfiles=MODE         use .rsyncsums to speedup --checksum mode\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");
-@@ -817,7 +819,7 @@ enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
-       OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_HELP,
+@@ -581,7 +582,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
        OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
        OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
--      OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG,
-+      OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_SUMFILES,
-       OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT,
-       OPT_SERVER, OPT_REFUSED_BASE = 9000};
-@@ -957,6 +959,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 },
+       OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
+-      OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
++      OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_SUMFILES,
+       OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
+       OPT_STOP_AFTER, OPT_STOP_AT,
+       OPT_REFUSED_BASE = 9000};
+@@ -738,6 +739,7 @@ static struct poptOption long_options[] = {
    {"no-c",             0,  POPT_ARG_VAL,    &always_checksum, 0, 0, 0 },
+   {"checksum-choice",  0,  POPT_ARG_STRING, &checksum_choice, 0, 0, 0 },
+   {"cc",               0,  POPT_ARG_STRING, &checksum_choice, 0, 0, 0 },
 +  {"sumfiles",         0,  POPT_ARG_STRING, 0, OPT_SUMFILES, 0, 0 },
-   {"block-size",      'B', POPT_ARG_LONG,   &block_size, 0, 0, 0 },
+   {"block-size",      'B', POPT_ARG_STRING, 0, OPT_BLOCK_SIZE, 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 },
-@@ -1682,6 +1685,23 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -1746,6 +1748,23 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                        }
                        break;
  
@@ -693,33 +697,92 @@ diff --git a/options.c b/options.c
                case OPT_INFO:
                        arg = poptGetOptArg(pc);
                        parse_output_words(info_words, info_levels, arg, USER_PRIORITY);
-@@ -1923,6 +1943,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -2099,6 +2118,9 @@ int parse_arguments(int *argc_p, const char ***argv_p)
        }
  #endif
  
 +      if (!always_checksum)
 +              checksum_files = CSF_IGNORE_FILES;
 +
-       if (block_size > MAX_BLOCK_SIZE) {
+       if (write_batch && read_batch) {
                snprintf(err_buf, sizeof err_buf,
-                        "--block-size=%lu is too large (max: %u)\n", block_size, MAX_BLOCK_SIZE);
+                       "--write-batch and --read-batch can not be used together\n");
+diff --git a/rsync.1.md b/rsync.1.md
+--- a/rsync.1.md
++++ b/rsync.1.md
+@@ -386,6 +386,7 @@ has its own detailed description later in this manpage.
+ --quiet, -q              suppress non-error messages
+ --no-motd                suppress daemon-mode MOTD
+ --checksum, -c           skip based on checksum, not mod-time & size
++--sumfiles=MODE          use .rsyncsums to speedup --checksum mode
+ --archive, -a            archive mode is -rlptgoD (no -A,-X,-U,-N,-H)
+ --no-OPTION              turn off an implied OPTION (e.g. --no-D)
+ --recursive, -r          recurse into directories
+@@ -778,6 +779,8 @@ expand it.
+     file that has the same size as the corresponding sender's file: files with
+     either a changed size or a changed checksum are selected for transfer.
++    See also the [`--sumfiles`](#opt) option for a way to use cached checksum data.
++
+     Note that rsync always verifies that each _transferred_ file was correctly
+     reconstructed on the receiving side by checking a whole-file checksum that
+     is generated as the file is transferred, but that automatic
+@@ -789,6 +792,38 @@ expand it.
+     option or an environment variable that is discussed in that option's
+     section.
++0.  `--sumfiles=MODE`
++
++    This option tells rsync to make use of any cached checksum information it
++    finds in per-directory .rsyncsums files when the current transfer is using
++    the [`--checksum`](#opt) option.  If the checksum data is up-to-date, it is
++    used instead of recomputing it, saving both disk I/O and CPU time.  If the
++    checksum data is missing or outdated, the checksum is computed just as it
++    would be if `--sumfiles` was not specified.
++
++    The MODE value is either "lax", for relaxed checking (which compares size
++    and mtime), "strict" (which also compares ctime and inode), or "none" to
++    ignore any .rsyncsums files ("none" is the default).  Rsync does not create
++    or update these files, but there is a perl script in the support directory
++    named "rsyncsums" that can be used for that.
++
++    This option has no effect unless [`--checksum`](#opt) (`-c`) was also
++    specified.  It also only affects the current side of the transfer, so if
++    you want the remote side to parse its own .rsyncsums files, specify the
++    option via [`--remote-option`](#opt) (`-M`) (e.g. "`-M--sumfiles=lax`").
++
++    To avoid transferring the system's checksum files, you can use an exclude
++    (e.g. [`--exclude=.rsyncsums`](#opt)).  To make this easier to type, you
++    can use a popt alias.  For instance, adding the following line in your
++    ~/.popt file defines a `--cs` option that enables lax checksum files and
++    excludes the checksum files:
++
++    >     rsync alias --cs -c --sumfiles=lax -M--sumfiles=lax -f-_.rsyncsums
++
++    An rsync daemon does not allow the client to control this setting, so see
++    the "checksum files" daemon parameter for information on how to make a
++    daemon use cached checksum data.
++
+ 0.  `--archive`, `-a`
+     This is equivalent to `-rlptgoD`.  It is a quick way of saying you want
 diff --git a/rsync.h b/rsync.h
 --- a/rsync.h
 +++ b/rsync.h
-@@ -771,6 +771,10 @@ extern int xattrs_ndx;
+@@ -903,6 +903,10 @@ extern int file_sum_extra_cnt;
  #define F_SUM(f) ((char*)OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) \
                                    + SUM_EXTRA_CNT - 1))
  
-+/* These are only valid on an entry read from a checksum file. */
++/* These are only valid on an entry derived from a checksum file. */
 +#define F_CTIME(f) OPT_EXTRA(f, LEN64_BUMP(f) + SUM_EXTRA_CNT)->unum
 +#define F_INODE(f) OPT_EXTRA(f, LEN64_BUMP(f) + SUM_EXTRA_CNT + 1)->unum
 +
  /* Some utility defines: */
  #define F_IS_ACTIVE(f) (f)->basename[0]
  #define F_IS_HLINKED(f) ((f)->flags & FLAG_HLINKED)
-@@ -967,6 +971,13 @@ typedef struct {
-       char fname[1]; /* has variable size */
- } relnamecache;
+@@ -1117,6 +1121,13 @@ typedef struct {
+ #define RELNAMECACHE_LEN (offsetof(relnamecache, fname))
+ #endif
  
 +#define CSF_ENABLE (1<<1)
 +#define CSF_LAX (1<<2)
@@ -731,97 +794,29 @@ diff --git a/rsync.h b/rsync.h
  #include "byteorder.h"
  #include "lib/mdigest.h"
  #include "lib/wildmatch.h"
-diff --git a/rsync.yo b/rsync.yo
---- a/rsync.yo
-+++ b/rsync.yo
-@@ -337,6 +337,7 @@ to the detailed description below for a complete description.  verb(
-  -q, --quiet                 suppress non-error messages
-      --no-motd               suppress daemon-mode MOTD (see caveat)
-  -c, --checksum              skip based on checksum, not mod-time & size
-+     --sumfiles=MODE         use .rsyncsums to speedup --checksum mode
-  -a, --archive               archive mode; equals -rlptgoD (no -H,-A,-X)
-      --no-OPTION             turn off an implied OPTION (e.g. --no-D)
-  -r, --recursive             recurse into directories
-@@ -606,9 +607,9 @@ uses a "quick check" that (by default) checks if each file's size and time
- of last modification match between the sender and receiver.  This option
- changes this to compare a 128-bit checksum for each file that has a
- matching size.  Generating the checksums means that both sides will expend
--a lot of disk I/O reading all the data in the files in the transfer (and
--this is prior to any reading that will be done to transfer changed files),
--so this can slow things down significantly.
-+a lot of disk I/O reading the data in all the files in the transfer, so
-+this can slow things down significantly (and this is prior to any reading
-+that will be done to transfer the files that have changed).
- The sending side generates its checksums while it is doing the file-system
- scan that builds the list of the available files.  The receiver generates
-@@ -616,6 +617,8 @@ its checksums when it is scanning for changed files, and will checksum any
- file that has the same size as the corresponding sender's file:  files with
- either a changed size or a changed checksum are selected for transfer.
-+See also the bf(--sumfiles) option for a way to use cached checksum data.
-+
- Note that rsync always verifies that each em(transferred) file was
- correctly reconstructed on the receiving side by checking a whole-file
- checksum that is generated as the file is transferred, but that
-@@ -625,6 +628,36 @@ option's before-the-transfer "Does this file need to be updated?" check.
- For protocol 30 and beyond (first supported in 3.0.0), the checksum used is
- MD5.  For older protocols, the checksum used is MD4.
+diff --git a/rsyncd.conf.5.md b/rsyncd.conf.5.md
+--- a/rsyncd.conf.5.md
++++ b/rsyncd.conf.5.md
+@@ -449,6 +449,19 @@ the values of parameters.  See the GLOBAL PARAMETERS section for more details.
+     the max connections limit is not exceeded for the modules sharing the lock
+     file.  The default is `/var/run/rsyncd.lock`.
  
-+dit(bf(--sumfiles=MODE)) This option tells rsync to make use of any cached
-+checksum information it finds in per-directory .rsyncsums files when the
-+current transfer is using the bf(--checksum) option.  If the checksum data
-+is up-to-date, it is used instead of recomputing it, saving both disk I/O
-+and CPU time.  If the checksum data is missing or outdated, the checksum is
-+computed just as it would be if bf(--sumfiles) was not specified.
-+
-+The MODE value is either "lax", for relaxed checking (which compares size
-+and mtime), "strict" (which also compares ctime and inode), or "none" to
-+ignore any .rsyncsums files ("none" is the default).  Rsync does not create
-+or update these files, but there is a perl script in the support directory
-+named "rsyncsums" that can be used for that.
-+
-+This option has no effect unless bf(--checksum, -c) was also specified.  It
-+also only affects the current side of the transfer, so if you want the
-+remote side to parse its own .rsyncsums files, specify the option via the
-+bf(--rsync-path) option (e.g. "--rsync-path="rsync --sumfiles=lax").
-+
-+To avoid transferring the system's checksum files, you can use an exclude
-+(e.g. bf(--exclude=.rsyncsums)).  To make this easier to type, you can use
-+a popt alias.  For instance, adding the following line in your ~/.popt file
-+defines a bf(--cc) option that enables lax checksum files and excludes the
-+checksum files:
-+
-+verb(  rsync alias --cc -c --sumfiles=lax --exclude=.rsyncsums)
-+
-+An rsync daemon does not allow the client to control this setting, so see
-+the "checksum files" daemon parameter for information on how to make a
-+daemon use cached checksum data.
-+
- dit(bf(-a, --archive)) This is equivalent to bf(-rlptgoD). It is a quick
- way of saying you want recursion and want to preserve almost
- everything (with -H being a notable omission).
-diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
---- a/rsyncd.conf.yo
-+++ b/rsyncd.conf.yo
-@@ -324,6 +324,17 @@ locking on this file to ensure that the max connections limit is not
- exceeded for the modules sharing the lock file.
- The default is tt(/var/run/rsyncd.lock).
++0.  `checksum files`
++
++    This parameter tells rsync to make use of any cached checksum information
++    it finds in per-directory .rsyncsums files when the current transfer is
++    using the `--checksum` option.  The value can be set to either "lax",
++    "strict", or "none".  See the client's `--sumfiles` option for what these
++    choices do.
++
++    Note also that the client's command-line option, `--sumfiles`, has no
++    effect on a daemon.  A daemon will only access checksum files if this
++    config option tells it to.  See also the `exclude` directive for a way to
++    hide the .rsyncsums files from the user.
++
+ 0.  `read only`
  
-+dit(bf(checksum files)) This parameter tells rsync to make use of any cached
-+checksum information it finds in per-directory .rsyncsums files when the
-+current transfer is using the bf(--checksum) option.  The value can be set
-+to either "lax", "strict", or "none" -- see the client's bf(--sumfiles)
-+option for what these choices do.
-+
-+Note also that the client's command-line option, bf(--sumfiles), has no
-+effect on a daemon.  A daemon will only access checksum files if this
-+config option tells it to.  See also the bf(exclude) directive for a way
-+to hide the .rsyncsums files from the user.
-+
- dit(bf(read only)) This parameter determines whether clients
- will be able to upload files or not. If "read only" is true then any
- attempted uploads will fail. If "read only" is false then uploads will
+     This parameter determines whether clients will be able to upload files or
 diff --git a/support/rsyncsums b/support/rsyncsums
 new file mode 100755
 --- /dev/null