./configure (optional if already run)
make
-based-on: 23a37ecac4bba997948fa30e72eb4aa8e317e394
+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;
extern int io_timeout;
extern int no_detach;
extern int write_batch;
-@@ -906,6 +908,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;
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
extern int inc_recurse;
extern int always_checksum;
+extern int basis_dir_cnt;
- extern int checksum_type;
extern int module_id;
extern int ignore_errors;
-@@ -61,6 +63,7 @@ extern int implied_dirs;
+ extern int numeric_ids;
+@@ -62,6 +64,7 @@ extern int implied_dirs;
extern int ignore_perishable;
extern int non_perishable_cnt;
extern int prune_empty_dirs;
extern int copy_links;
extern int copy_unsafe_links;
extern int protocol_version;
-@@ -72,6 +75,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;
-@@ -89,6 +93,20 @@ extern int filesfrom_convert;
+@@ -92,6 +96,20 @@ extern int filesfrom_convert;
extern iconv_t ic_send, ic_recv;
#endif
#define PTR_SIZE (sizeof (struct file_struct *))
int io_error;
-@@ -133,7 +151,11 @@ 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)
-@@ -326,6 +348,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;
}
+{
+ 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;
+
+ while (fgets(line, sizeof line, fp)) {
+ cp = line;
-+ if (checksum_type == 5) {
++ 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 == ' ') {}
+ } else {
+ 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
+ break;
+ while (*++cp == ' ') {}
+
-+ if (checksum_type != 5) {
++ 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 == ' ') {}
/* 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
-@@ -1154,7 +1408,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;
struct file_struct *file;
char thisname[MAXPATHLEN];
char linkname[MAXPATHLEN];
-@@ -1300,9 +1554,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;
basename_len = strlen(basename) + 1; /* count the '\0' */
#ifdef SUPPORT_LINKS
-@@ -1320,11 +1581,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 EXTRA_ROUNDING > 0
if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
-@@ -1411,8 +1669,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 (unsort_ndx)
F_NDX(file) = stats.num_dirs;
-@@ -2619,7 +2883,7 @@ struct file_list *recv_file_list(int f, int dir_ndx)
+@@ -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(). */
if (protocol_version < 30) {
/* Recv the io_error flag */
-@@ -2870,7 +3134,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. */
{
char fbuf[MAXPATHLEN];
int i, prev_i;
-@@ -2921,7 +3185,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. */
struct file_struct *fp = flist->sorted[j];
if (!S_ISDIR(fp->mode))
keep = i, drop = j;
-@@ -2937,8 +3201,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;
rprintf(FINFO,
"removing duplicate name %s from file list (%d)\n",
f_name(file, fbuf), drop + flist->ndx_start);
-@@ -2960,7 +3224,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;
diff --git a/generator.c b/generator.c
--- a/generator.c
+++ b/generator.c
-@@ -52,6 +52,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;
extern int remove_source_files;
extern int delay_updates;
extern int update_only;
-@@ -578,7 +579,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;
-@@ -587,7 +588,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, 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;
- }
+ 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;
+ }
-@@ -884,7 +888,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 (!unchanged_file(cmpbuf, file, &sxp->st))
-+ if (!unchanged_file(cmpbuf, file, &sxp->st, j+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;
-@@ -1195,7 +1199,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+@@ -1079,7 +1083,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
+ match_level = 1;
+ 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;
+@@ -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];
struct file_struct *fuzzy_file = NULL;
int fd = -1, f_copy = -1;
stat_x sx, real_sx;
-@@ -1306,8 +1310,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;
}
}
#ifdef SUPPORT_ACLS
if (!preserve_perms)
dflt_perms = default_perms_for_dir(dn);
-@@ -1315,7 +1320,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++) {
-@@ -1327,7 +1332,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);
-@@ -1772,7 +1780,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 (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
-@@ -22,6 +22,12 @@ isDigit(const char *ptr)
- return isdigit(*(unsigned char *)ptr);
- }
-
-+static inline int
-+isXDigit(const char *ptr)
-+{
-+ return isxdigit(*(unsigned char *)ptr);
-+}
-+
- static inline int
- isPrint(const char *ptr)
- {
diff --git a/loadparm.c b/loadparm.c
--- a/loadparm.c
+++ b/loadparm.c
-@@ -173,6 +173,7 @@ typedef struct {
- BOOL temp_dir_EXP;
- BOOL uid_EXP;
-
-+ int checksum_files;
- int max_connections;
- int max_verbosity;
- int syslog_facility;
-@@ -286,6 +287,7 @@ static const all_vars Defaults = {
- /* temp_dir_EXP; */ False,
- /* uid_EXP; */ False,
-
-+ /* checksum_files; */ CSF_IGNORE_FILES,
- /* max_connections; */ 0,
- /* max_verbosity; */ 1,
- /* syslog_facility; */ LOG_DAEMON,
-@@ -388,6 +390,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},
-@@ -402,6 +411,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},
-@@ -566,6 +576,7 @@ FN_LOCAL_STRING(lp_syslog_tag, syslog_tag)
- 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
-@@ -118,6 +118,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;
int max_delete = INT_MIN;
OFF_T max_size = -1;
OFF_T min_size = -1;
-@@ -754,6 +755,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");
-@@ -905,7 +907,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_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS,
- OPT_SERVER, OPT_REFUSED_BASE = 9000};
-@@ -1055,6 +1057,7 @@ static struct poptOption long_options[] = {
+ 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 },
-@@ -1877,6 +1880,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;
case OPT_INFO:
arg = poptGetOptArg(pc);
parse_output_words(info_words, info_levels, arg, USER_PRIORITY);
-@@ -2146,6 +2166,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) {
- /* We may not know the real protocol_version at this point if this is the client
- * option parsing, but we still want to check it so that the client can specify
+ if (write_batch && read_batch) {
+ snprintf(err_buf, sizeof err_buf,
+ "--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
-@@ -826,6 +826,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)
-@@ -1028,6 +1032,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)
#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
-@@ -346,6 +346,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
-@@ -645,9 +646,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
-@@ -655,6 +656,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
-@@ -665,6 +668,36 @@ The checksum used is auto-negotiated between the client and the server, but
- can be overridden using either the bf(--checksum-choice) option or an
- environment variable that is discussed in that option's section.
-
-+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
-@@ -355,6 +355,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).
-
-+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
+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`.
+
++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`
+
+ 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