This builds on the checksum-reading patch and adds the ability to
create and/or update the .rsyncsums files using extended mode args to
the --sumfiles=MODE option and the "checksum files = MODE" daemon
parameter.
CAUTION: This patch is only lightly tested. If you're interested
in using it, please help out.
To use this patch, run these commands for a successful build:
patch -p1 <patches/checksum-reading.diff
patch -p1 <patches/checksum-updating.diff
./configure (optional if already run)
make
TODO:
- Fix the code that removes .rsyncsums files when a dir becomes empty.
based-on: patch/master/checksum-reading
diff --git a/flist.c b/flist.c
--- a/flist.c
+++ b/flist.c
@@ -27,6 +27,7 @@
#include "inums.h"
#include "io.h"
+extern int dry_run;
extern int am_root;
extern int am_server;
extern int am_daemon;
@@ -108,6 +109,9 @@ extern iconv_t ic_send, ic_recv;
#define PTR_SIZE (sizeof (struct file_struct *))
+#define FLAG_SUM_MISSING (1<<1) /* F_SUM() data is undefined */
+#define FLAG_SUM_KEEP (1<<2) /* keep entry when rewriting */
+
int io_error;
int flist_csum_len;
dev_t filesystem_dev; /* used to implement -x */
@@ -149,8 +153,13 @@ static char empty_sum[MAX_DIGEST_LEN];
static int flist_count_offset; /* for --delete --progress */
static int show_filelist_progress;
+#define REGULAR_SKIPPED(flist) ((flist)->to_redo)
+
static struct csum_cache {
struct file_list *flist;
+ const char *dirname;
+ int checksum_matches;
+ int checksum_updates;
} *csum_cache = NULL;
static void flist_sort_and_clean(struct file_list *flist, int flags);
@@ -347,7 +356,79 @@ static void flist_done_allocating(struct file_list *flist)
flist->pool_boundary = ptr;
}
-void reset_checksum_cache()
+static void checksum_filename(int slot, const char *dirname, char *fbuf)
+{
+ if (dirname && *dirname) {
+ unsigned int len;
+ if (slot) {
+ len = strlcpy(fbuf, basis_dir[slot-1], MAXPATHLEN);
+ if (len >= MAXPATHLEN)
+ return;
+ } else
+ len = 0;
+ if (pathjoin(fbuf+len, MAXPATHLEN-len, dirname, RSYNCSUMS_FILE) >= MAXPATHLEN-len)
+ return;
+ } else
+ strlcpy(fbuf, RSYNCSUMS_FILE, MAXPATHLEN);
+}
+
+static void write_checksums(int slot, struct file_list *flist, int whole_dir)
+{
+ int i;
+ FILE *out_fp;
+ char fbuf[MAXPATHLEN];
+ int new_entries = csum_cache[slot].checksum_updates != 0;
+ int counts_match = flist->used == csum_cache[slot].checksum_matches;
+ int no_skipped = whole_dir && REGULAR_SKIPPED(flist) == 0;
+ const char *dirname = csum_cache[slot].dirname;
+
+ flist_sort_and_clean(flist, 0);
+
+ if (dry_run && !(checksum_files & CSF_AFFECT_DRYRUN))
+ return;
+
+ checksum_filename(slot, dirname, fbuf);
+
+ if (flist->high - flist->low < 0 && no_skipped) {
+ unlink(fbuf);
+ return;
+ }
+
+ if (!new_entries && (counts_match || !whole_dir))
+ return;
+
+ if (!(out_fp = fopen(fbuf, "w")))
+ return;
+
+ for (i = flist->low; i <= flist->high; i++) {
+ struct file_struct *file = flist->sorted[i];
+ const char *cp = F_SUM(file);
+ const char *end = cp + flist_csum_len;
+ const char *alt_sum = file->basename + strlen(file->basename) + 1;
+ if (whole_dir && !(file->flags & FLAG_SUM_KEEP))
+ continue;
+ if (checksum_type == 5)
+ fprintf(out_fp, "%s ", alt_sum);
+ if (file->flags & FLAG_SUM_MISSING) {
+ do {
+ fputs("==", out_fp);
+ } while (++cp != end);
+ } else {
+ do {
+ fprintf(out_fp, "%02x", (int)CVAL(cp, 0));
+ } while (++cp != end);
+ }
+ if (checksum_type != 5)
+ fprintf(out_fp, " %s", alt_sum);
+ fprintf(out_fp, " %10.0f %10.0f %10lu %10lu %s\n",
+ (double)F_LENGTH(file), (double)file->modtime,
+ (long)F_CTIME(file), (long)F_INODE(file), file->basename);
+ }
+
+ fclose(out_fp);
+}
+
+void reset_checksum_cache(int whole_dir)
{
int slot, slots = am_sender ? 1 : basis_dir_cnt + 1;
@@ -361,6 +442,9 @@ void reset_checksum_cache()
struct file_list *flist = csum_cache[slot].flist;
if (flist) {
+ if (checksum_files & CSF_UPDATE && flist->next)
+ write_checksums(slot, flist, whole_dir);
+
/* Reset the pool memory and empty the file-list array. */
pool_free_old(flist->file_pool,
pool_boundary(flist->file_pool, 0));
@@ -371,6 +455,10 @@ void reset_checksum_cache()
flist->low = 0;
flist->high = -1;
flist->next = NULL;
+
+ csum_cache[slot].checksum_matches = 0;
+ csum_cache[slot].checksum_updates = 0;
+ REGULAR_SKIPPED(flist) = 0;
}
}
@@ -378,7 +466,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,
- const char *sum)
+ const char *sum, const char *alt_sum, int flags)
{
struct file_struct *file;
int alloc_len, extra_len;
@@ -395,7 +483,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
- alloc_len = FILE_STRUCT_LEN + extra_len + basename_len;
+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len + flist_csum_len*2 + 1;
bp = pool_alloc(flist->file_pool, alloc_len, "add_checksum");
memset(bp, 0, extra_len + FILE_STRUCT_LEN);
@@ -404,7 +492,14 @@ static int add_checksum(struct file_list *flist, const char *dirname,
bp += FILE_STRUCT_LEN;
memcpy(bp, basename, basename_len);
+ if (alt_sum)
+ strlcpy(bp+basename_len, alt_sum, flist_csum_len*2 + 1);
+ else {
+ memset(bp+basename_len, '=', flist_csum_len*2);
+ bp[basename_len+flist_csum_len*2] = '\0';
+ }
+ file->flags = flags;
file->mode = S_IFREG;
file->modtime = mtime;
file->len32 = (uint32)file_length;
@@ -433,10 +528,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;
- int len, i;
time_t mtime;
+ int len, i, flags;
OFF_T file_length;
uint32 ctime, inode;
+ const char *alt_sum = NULL;
int dlen = dirname ? strlcpy(fbuf, dirname, sizeof fbuf) : 0;
if (dlen >= (int)(sizeof fbuf - 1 - RSYNCSUMS_LEN))
@@ -457,7 +553,7 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
while (fgets(line, sizeof line, fp)) {
cp = line;
if (checksum_type == 5) {
- char *alt_sum = cp;
+ alt_sum = cp;
if (*cp == '=')
while (*++cp == '=') {}
else
@@ -468,7 +564,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
}
if (*cp == '=') {
- continue;
+ for (i = 0; i < flist_csum_len*2; i++, cp++) {
+ if (*cp != '=') {
+ cp = "";
+ break;
+ }
+ }
+ memset(sum, 0, flist_csum_len);
+ flags = FLAG_SUM_MISSING;
} else {
for (i = 0; i < flist_csum_len*2; i++, cp++) {
int x;
@@ -486,13 +589,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
else
sum[i/2] = x << 4;
}
+ flags = 0;
}
if (*cp != ' ')
break;
while (*++cp == ' ') {}
if (checksum_type != 5) {
- char *alt_sum = cp;
+ alt_sum = cp;
if (*cp == '=')
while (*++cp == '=') {}
else
@@ -542,24 +646,112 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
continue;
strlcpy(fbuf+dlen, cp, sizeof fbuf - dlen);
+ if (is_excluded(fbuf, 0, ALL_FILTERS)) {
+ flags |= FLAG_SUM_KEEP;
+ csum_cache[slot].checksum_matches++;
+ }
add_checksum(flist, dirname, cp, len, file_length,
mtime, ctime, inode,
- sum);
+ sum, alt_sum, flags);
}
fclose(fp);
flist_sort_and_clean(flist, CLEAN_KEEP_LAST);
}
+void set_cached_checksum(struct file_list *file_flist, struct file_struct *file)
+{
+ int j;
+ FILE *out_fp;
+ STRUCT_STAT st;
+ char fbuf[MAXPATHLEN];
+ const char *fn = f_name(file, NULL);
+ struct file_list *flist = csum_cache[0].flist;
+
+ if (dry_run && !(checksum_files & CSF_AFFECT_DRYRUN))
+ return;
+
+ if (stat(fn, &st) < 0)
+ return;
+
+ checksum_filename(0, file->dirname, fbuf);
+
+ if (file_flist != flist->next) {
+ const char *cp = F_SUM(file);
+ const char *end = cp + flist_csum_len;
+
+ if (!(out_fp = fopen(fbuf, "a")))
+ return;
+
+ if (checksum_type == 5) {
+ for (j = 0; j < flist_csum_len; j++)
+ fputs("==", out_fp);
+ fputc(' ', out_fp);
+ }
+ do {
+ fprintf(out_fp, "%02x", (int)CVAL(cp, 0));
+ } while (++cp != end);
+ if (checksum_type != 5) {
+ fputc(' ', out_fp);
+ for (j = 0; j < flist_csum_len; j++)
+ fputs("==", out_fp);
+ }
+ fprintf(out_fp, " %10.0f %10.0f %10lu %10lu %s\n",
+ (double)st.st_size, (double)st.st_mtime,
+ (long)(uint32)st.st_ctime, (long)(uint32)st.st_ino,
+ file->basename);
+
+ fclose(out_fp);
+ return;
+ }
+
+ if ((j = flist_find(flist, file)) >= 0) {
+ struct file_struct *fp = flist->sorted[j];
+ int inc = 0;
+ if (F_LENGTH(fp) != st.st_size) {
+ fp->len32 = (uint32)st.st_size;
+ if (st.st_size > 0xFFFFFFFFu) {
+ OPT_EXTRA(fp, 0)->unum = (uint32)(st.st_size >> 32);
+ fp->flags |= FLAG_LENGTH64;
+ } else
+ fp->flags &= FLAG_LENGTH64;
+ inc = 1;
+ }
+ if (fp->modtime != st.st_mtime) {
+ fp->modtime = st.st_mtime;
+ inc = 1;
+ }
+ if (F_CTIME(fp) != (uint32)st.st_ctime) {
+ F_CTIME(fp) = (uint32)st.st_ctime;
+ inc = 1;
+ }
+ if (F_INODE(fp) != (uint32)st.st_ino) {
+ F_INODE(fp) = (uint32)st.st_ino;
+ inc = 1;
+ }
+ memcpy(F_SUM(fp), F_SUM(file), MAX_DIGEST_LEN);
+ csum_cache[0].checksum_updates += inc;
+ fp->flags &= ~FLAG_SUM_MISSING;
+ fp->flags |= FLAG_SUM_KEEP;
+ return;
+ }
+
+ csum_cache[0].checksum_updates +=
+ add_checksum(flist, file->dirname, file->basename, strlen(file->basename) + 1,
+ st.st_size, (uint32)st.st_mtime, (uint32)st.st_ctime,
+ st.st_ino, F_SUM(file), NULL, FLAG_SUM_KEEP);
+}
+
void get_cached_checksum(int slot, const char *fname, struct file_struct *file,
- STRUCT_STAT *stp, char *sum_buf)
+ int basename_len, STRUCT_STAT *stp, char *sum_buf)
{
struct file_list *flist = csum_cache[slot].flist;
int j;
if (!flist->next) {
flist->next = cur_flist; /* next points from checksum flist to file flist */
+ csum_cache[slot].dirname = file->dirname;
read_checksums(slot, flist, file->dirname);
}
@@ -571,12 +763,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))) {
- memcpy(sum_buf, F_SUM(fp), MAX_DIGEST_LEN);
+ if (fp->flags & FLAG_SUM_MISSING) {
+ fp->flags &= ~FLAG_SUM_MISSING;
+ csum_cache[slot].checksum_updates++;
+ file_checksum(fname, stp, sum_buf);
+ memcpy(F_SUM(fp), sum_buf, MAX_DIGEST_LEN);
+ } else {
+ csum_cache[slot].checksum_matches++;
+ memcpy(sum_buf, F_SUM(fp), MAX_DIGEST_LEN);
+ }
+ fp->flags |= FLAG_SUM_KEEP;
return;
}
+ clear_file(fp);
}
file_checksum(fname, stp, sum_buf);
+
+ if (checksum_files & CSF_UPDATE) {
+ if (basename_len < 0)
+ basename_len = strlen(file->basename) + 1;
+ csum_cache[slot].checksum_updates +=
+ add_checksum(flist, file->dirname, file->basename, basename_len,
+ stp->st_size, stp->st_mtime, (uint32)stp->st_ctime,
+ (uint32)stp->st_ino, sum_buf, NULL, FLAG_SUM_KEEP);
+ }
}
/* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
@@ -1483,6 +1694,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)) {
if (ignore_perishable)
non_perishable_cnt++;
+ if (S_ISREG(st.st_mode))
+ REGULAR_SKIPPED(flist)++;
return NULL;
}
@@ -1529,13 +1742,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)
- reset_checksum_cache();
+ reset_checksum_cache(0);
}
} else {
basename = thisname;
if (checksum_files && am_sender && flist && lastdir_len == -2) {
lastdir_len = -1;
- reset_checksum_cache();
+ reset_checksum_cache(0);
}
}
basename_len = strlen(basename) + 1; /* count the '\0' */
@@ -1643,7 +1856,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)
- get_cached_checksum(0, thisname, file, &st, tmp_sum);
+ get_cached_checksum(0, thisname, file, basename_len, &st, tmp_sum);
else
file_checksum(thisname, &st, tmp_sum);
if (sender_keeps_checksum)
@@ -2019,6 +2232,9 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
closedir(d);
+ if (checksum_files & CSF_UPDATE && am_sender && f >= 0)
+ reset_checksum_cache(1);
+
if (f >= 0 && recurse && !divert_dirs) {
int i, end = flist->used - 1;
/* send_if_directory() bumps flist->used, so use "end". */
@@ -2678,6 +2894,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i());
}
+ if (checksum_files & CSF_UPDATE && flist_eof)
+ reset_checksum_cache(0); /* writes any last updates */
+
return flist;
}
diff --git a/generator.c b/generator.c
--- a/generator.c
+++ b/generator.c
@@ -111,6 +111,7 @@ static int dir_tweaking;
static int symlink_timeset_failed_flags;
static int need_retouch_dir_times;
static int need_retouch_dir_perms;
+static int started_whole_dir, upcoming_whole_dir;
static const char *solo_file = NULL;
enum nonregtype {
@@ -585,7 +586,7 @@ int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st, int slot
if (always_checksum > 0 && S_ISREG(st->st_mode)) {
char sum[MAX_DIGEST_LEN];
if (checksum_files && slot >= 0)
- get_cached_checksum(slot, fn, file, st, sum);
+ get_cached_checksum(slot, fn, file, -1, st, sum);
else
file_checksum(fn, st, sum);
return memcmp(sum, F_SUM(file), flist_csum_len) == 0;
@@ -1321,7 +1322,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
}
if (checksum_files) {
- reset_checksum_cache();
+ reset_checksum_cache(started_whole_dir);
+ started_whole_dir = upcoming_whole_dir;
}
need_new_dirscan = 0;
}
@@ -1495,6 +1497,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;
prior_dir_file = file;
goto cleanup;
}
@@ -1774,6 +1777,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 | maybe_ATTRS_SET_NANO);
+ if (checksum_files & CSF_UPDATE)
+ set_cached_checksum(cur_flist, file);
if (itemizing)
itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
#ifdef SUPPORT_HARD_LINKS
@@ -2267,6 +2272,7 @@ void generate_files(int f_out, const char *local_name)
} else
change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
}
+ upcoming_whole_dir = fp->flags & FLAG_CONTENT_DIR ? 1 : 0;
}
for (i = cur_flist->low; i <= cur_flist->high; i++) {
struct file_struct *file = cur_flist->sorted[i];
@@ -2361,6 +2367,9 @@ void generate_files(int f_out, const char *local_name)
wait_for_receiver();
}
+ if (checksum_files)
+ reset_checksum_cache(started_whole_dir);
+
info_levels[INFO_FLIST] = save_info_flist;
info_levels[INFO_PROGRESS] = save_info_progress;
diff --git a/io.c b/io.c
--- a/io.c
+++ b/io.c
@@ -53,6 +53,7 @@ extern int read_batch;
extern int compat_flags;
extern int protect_args;
extern int checksum_seed;
+extern int checksum_files;
extern int protocol_version;
extern int remove_source_files;
extern int preserve_hard_links;
@@ -1050,6 +1051,9 @@ static void got_flist_entry_status(enum festatus status, int ndx)
if (inc_recurse)
flist->in_progress++;
}
+ } else if (checksum_files & CSF_UPDATE) {
+ struct file_struct *file = flist->files[ndx - flist->ndx_start];
+ set_cached_checksum(flist, file);
}
#endif
break;
diff --git a/loadparm.c b/loadparm.c
--- a/loadparm.c
+++ b/loadparm.c
@@ -324,6 +324,10 @@ static struct enum_list enum_csum_modes[] = {
{ CSF_IGNORE_FILES, "none" },
{ CSF_LAX_MODE, "lax" },
{ CSF_STRICT_MODE, "strict" },
+ { CSF_LAX_MODE|CSF_UPDATE, "+lax" },
+ { CSF_STRICT_MODE|CSF_UPDATE, "+strict" },
+ { CSF_LAX_MODE|CSF_UPDATE|CSF_AFFECT_DRYRUN, "++lax" },
+ { CSF_STRICT_MODE|CSF_UPDATE|CSF_AFFECT_DRYRUN, "++strict" },
{ -1, NULL }
};
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
@@ -1701,7 +1701,15 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case OPT_SUMFILES:
arg = poptGetOptArg(pc);
- checksum_files = 0;
+ if (*arg == '+') {
+ arg++;
+ checksum_files = CSF_UPDATE;
+ if (*arg == '+') {
+ arg++;
+ checksum_files |= CSF_AFFECT_DRYRUN;
+ }
+ } else
+ checksum_files = 0;
if (strcmp(arg, "lax") == 0)
checksum_files |= CSF_LAX_MODE;
else if (strcmp(arg, "strict") == 0)
diff --git a/receiver.c b/receiver.c
--- a/receiver.c
+++ b/receiver.c
@@ -49,6 +49,7 @@ extern int sparse_files;
extern int preallocate_files;
extern int keep_partial;
extern int checksum_seed;
+extern int checksum_files;
extern int whole_file;
extern int inplace;
extern int allowed_lull;
@@ -429,7 +430,7 @@ static void handle_delayed_updates(char *local_name)
"rename failed for %s (from %s)",
full_fname(fname), partialptr);
} else {
- if (remove_source_files
+ if (remove_source_files || checksum_files & CSF_UPDATE
|| (preserve_hard_links && F_IS_HLINKED(file)))
send_msg_int(MSG_SUCCESS, ndx);
handle_partial_dir(partialptr, PDIR_DELETE);
@@ -891,7 +892,7 @@ int recv_files(int f_in, int f_out, char *local_name)
case 2:
break;
case 1:
- if (remove_source_files || inc_recurse
+ if (remove_source_files || inc_recurse || checksum_files & CSF_UPDATE
|| (preserve_hard_links && F_IS_HLINKED(file)))
send_msg_int(MSG_SUCCESS, ndx);
break;
diff --git a/rsync.h b/rsync.h
--- a/rsync.h
+++ b/rsync.h
@@ -987,6 +987,8 @@ typedef struct {
#define CSF_ENABLE (1<<1)
#define CSF_LAX (1<<2)
+#define CSF_UPDATE (1<<3)
+#define CSF_AFFECT_DRYRUN (1<<4)
#define CSF_IGNORE_FILES 0
#define CSF_LAX_MODE (CSF_ENABLE|CSF_LAX)
diff --git a/rsync.yo b/rsync.yo
--- a/rsync.yo
+++ b/rsync.yo
@@ -662,9 +662,13 @@ 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.
+ignore any .rsyncsums files ("none" is the default).
+If you want rsync to create and/or update these files, specify a prefixed
+plus ("+lax" or "+strict").
+Adding a second prefixed '+' causes the checksum-file updates to happen
+even when the transfer is in bf(--dry-run) mode ("++lax" or "++strict").
+There is also a perl script in the support directory named "rsyncsums"
+that can be used to update the .rsyncsums files.
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
diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
--- a/rsyncd.conf.yo
+++ b/rsyncd.conf.yo
@@ -358,13 +358,15 @@ 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.
+to either "lax", "strict", "+lax", "+strict", "++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.
+config option tells it to. You can configure updating of the .rsyncsums
+files even if the module itself is configured to be read-only. 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
diff -Nurp a/proto.h b/proto.h
--- a/proto.h
+++ b/proto.h
@@ -89,9 +89,10 @@ int unmap_file(struct map_struct *map);
void init_flist(void);
void show_flist_stats(void);
int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks);
-void reset_checksum_cache();
+void reset_checksum_cache(int whole_dir);
+void set_cached_checksum(struct file_list *file_flist, struct file_struct *file);
void get_cached_checksum(int slot, const char *fname, struct file_struct *file,
- STRUCT_STAT *stp, char *sum_buf);
+ int basename_len, STRUCT_STAT *stp, char *sum_buf);
int change_pathname(struct file_struct *file, const char *dir, int dirlen);
struct file_struct *make_file(const char *fname, struct file_list *flist,
STRUCT_STAT *stp, int flags, int filter_level);
diff -Nurp a/rsync.1 b/rsync.1
--- a/rsync.1
+++ b/rsync.1
@@ -765,9 +765,13 @@ computed just as it would be if \fB\-\-s
.IP
The MODE value is either \(dq\&lax\(dq\&, for relaxed checking (which compares size
and mtime), \(dq\&strict\(dq\& (which also compares ctime and inode), or \(dq\&none\(dq\& to
-ignore any .rsyncsums files (\(dq\&none\(dq\& is the default). Rsync does not create
-or update these files, but there is a perl script in the support directory
-named \(dq\&rsyncsums\(dq\& that can be used for that.
+ignore any .rsyncsums files (\(dq\&none\(dq\& is the default).
+If you want rsync to create and/or update these files, specify a prefixed
+plus (\(dq\&+lax\(dq\& or \(dq\&+strict\(dq\&).
+Adding a second prefixed \(cq\&+\(cq\& causes the checksum\-file updates to happen
+even when the transfer is in \fB\-\-dry\-run\fP mode (\(dq\&++lax\(dq\& or \(dq\&++strict\(dq\&).
+There is also a perl script in the support directory named \(dq\&rsyncsums\(dq\&
+that can be used to update the .rsyncsums files.
.IP
This option has no effect unless \fB\-\-checksum, \-c\fP was also specified. It
also only affects the current side of the transfer, so if you want the
diff -Nurp a/rsyncd.conf.5 b/rsyncd.conf.5
--- a/rsyncd.conf.5
+++ b/rsyncd.conf.5
@@ -403,13 +403,15 @@ The default is \f(CW/var/run/rsyncd.lock
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 \fB\-\-checksum\fP option. The value can be set
-to either \(dq\&lax\(dq\&, \(dq\&strict\(dq\&, or \(dq\&none\(dq\& \-\- see the client\(cq\&s \fB\-\-sumfiles\fP
-option for what these choices do.
+to either \(dq\&lax\(dq\&, \(dq\&strict\(dq\&, \(dq\&+lax\(dq\&, \(dq\&+strict\(dq\&, \(dq\&++lax\(dq\&, \(dq\&++strict\(dq\&, or
+\(dq\&none\(dq\&. See the client\(cq\&s \fB\-\-sumfiles\fP option for what these choices do.
.IP
Note also that the client\(cq\&s command\-line option, \fB\-\-sumfiles\fP, has no
effect on a daemon. A daemon will only access checksum files if this
-config option tells it to. See also the \fBexclude\fP directive for a way
-to hide the .rsyncsums files from the user.
+config option tells it to. You can configure updating of the .rsyncsums
+files even if the module itself is configured to be read\-only. See also
+the \fBexclude\fP directive for a way to hide the .rsyncsums files from the
+user.
.IP
.IP "\fBread only\fP"
This parameter determines whether clients