+ return flist;
+ }
+
+diff --git a/generator.c b/generator.c
+--- a/generator.c
++++ b/generator.c
+@@ -114,6 +114,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;
+
+ /* For calling delete_item() and delete_dir_contents(). */
+@@ -726,7 +727,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->st_size, sum);
+ return memcmp(sum, F_SUM(file), checksum_len) == 0;
+@@ -1369,7 +1370,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, 1);
+ }
+ if (checksum_files) {
+- reset_checksum_cache();
++ reset_checksum_cache(started_whole_dir);
++ started_whole_dir = upcoming_whole_dir;
+ }
+ need_new_dirscan = 0;
+ }
+@@ -1519,6 +1521,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
+ else
+ change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
+ }
++ upcoming_whole_dir = file->flags & FLAG_CONTENT_DIR && f_out != -1 ? 1 : 0;
+ goto cleanup;
+ }
+
+@@ -1811,6 +1814,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);
++ 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
+@@ -2252,6 +2257,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];
+@@ -2338,6 +2344,9 @@ void generate_files(int f_out, const char *local_name)
+ wait_for_receiver();
+ }
+
++ if (checksum_files)
++ reset_checksum_cache(started_whole_dir);
++
+ do_progress = save_do_progress;
+ if (delete_during == 2)
+ do_delayed_deletions(fbuf);
+diff --git a/io.c b/io.c
+--- a/io.c
++++ b/io.c
+@@ -50,6 +50,7 @@ extern int read_batch;
+ extern int csum_length;
+ 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;
+@@ -161,6 +162,9 @@ static void got_flist_entry_status(enum festatus status, const char *buf)
+ flist_ndx_push(&hlink_list, ndx);
+ flist->in_progress++;
+ }
++ } else if (checksum_files & CSF_UPDATE) {
++ struct file_struct *file = flist->files[ndx - flist->ndx_start];
++ set_cached_checksum(flist, file);
+ }
+ break;
+ case FES_REDO:
+diff --git a/loadparm.c b/loadparm.c
+--- a/loadparm.c
++++ b/loadparm.c
+@@ -300,6 +300,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
+@@ -1234,7 +1234,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
+@@ -47,6 +47,7 @@ extern int append_mode;
+ extern int sparse_files;
+ extern int keep_partial;
+ extern int checksum_seed;
++extern int checksum_files;
+ extern int inplace;
+ extern int delay_updates;
+ extern mode_t orig_umask;
+@@ -343,7 +344,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);
+@@ -758,7 +759,7 @@ int recv_files(int f_in, 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
+@@ -885,6 +885,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
+@@ -548,9 +548,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
+@@ -284,13 +284,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