Updated patches to work with the current trunk.
[rsync/rsync-patches.git] / checksum-updating.diff
CommitLineData
f9df736a
WD
1This builds on the checksum-reading patch and adds the ability to
2create and/or update the .rsyncsums files using extended mode args to
3the --sumfiles=MODE option and the "checksum files = MODE" daemon
4parameter.
5
6CAUTION: This patch is only lightly tested. If you're interested
7in using it, please help out.
ae10e51e
WD
8
9To use this patch, run these commands for a successful build:
10
cc3e685d 11 patch -p1 <patches/checksum-reading.diff
ae10e51e
WD
12 patch -p1 <patches/checksum-updating.diff
13 ./configure (optional if already run)
14 make
15
f9df736a
WD
16TODO:
17
18 - Fix the code that removes .rsyncsums files when a dir becomes empty.
19
cc3e685d 20diff --git a/flist.c b/flist.c
fc557362 21index 8a42d80..d65c475 100644
cc3e685d
WD
22--- a/flist.c
23+++ b/flist.c
fc557362
WD
24@@ -27,6 +27,7 @@
25 #include "inums.h"
ae10e51e
WD
26 #include "io.h"
27
ae10e51e 28+extern int dry_run;
ae10e51e
WD
29 extern int am_root;
30 extern int am_server;
cdcd2137 31 extern int am_daemon;
fc557362 32@@ -99,6 +100,9 @@ extern iconv_t ic_send, ic_recv;
56522462
WD
33
34 #define PTR_SIZE (sizeof (struct file_struct *))
35
edf38a9d
WD
36+#define FLAG_SUM_MISSING (1<<1) /* F_SUM() data is undefined */
37+#define FLAG_SUM_KEEP (1<<2) /* keep entry when rewriting */
56522462
WD
38+
39 int io_error;
40 int checksum_len;
41 dev_t filesystem_dev; /* used to implement -x */
fc557362
WD
42@@ -137,8 +141,13 @@ static char tmp_sum[MAX_DIGEST_LEN];
43 static char empty_sum[MAX_DIGEST_LEN];
ae10e51e 44 static int flist_count_offset; /* for --delete --progress */
213d4328 45
f9df736a
WD
46+#define REGULAR_SKIPPED(flist) ((flist)->to_redo)
47+
48 static struct csum_cache {
49 struct file_list *flist;
50+ const char *dirname;
51+ int checksum_matches;
52+ int checksum_updates;
53 } *csum_cache = NULL;
213d4328 54
f9df736a 55 static void flist_sort_and_clean(struct file_list *flist, int flags);
fc557362 56@@ -356,7 +365,79 @@ static void flist_done_allocating(struct file_list *flist)
f9df736a 57 flist->pool_boundary = ptr;
213d4328
WD
58 }
59
f9df736a
WD
60-void reset_checksum_cache()
61+static void checksum_filename(int slot, const char *dirname, char *fbuf)
071bf6df 62+{
f9df736a
WD
63+ if (dirname && *dirname) {
64+ unsigned int len;
65+ if (slot) {
66+ len = strlcpy(fbuf, basis_dir[slot-1], MAXPATHLEN);
67+ if (len >= MAXPATHLEN)
68+ return;
69+ } else
70+ len = 0;
71+ if (pathjoin(fbuf+len, MAXPATHLEN-len, dirname, RSYNCSUMS_FILE) >= MAXPATHLEN-len)
72+ return;
73+ } else
74+ strlcpy(fbuf, RSYNCSUMS_FILE, MAXPATHLEN);
75+}
071bf6df 76+
f9df736a
WD
77+static void write_checksums(int slot, struct file_list *flist, int whole_dir)
78+{
79+ int i;
80+ FILE *out_fp;
81+ char fbuf[MAXPATHLEN];
82+ int new_entries = csum_cache[slot].checksum_updates != 0;
83+ int counts_match = flist->used == csum_cache[slot].checksum_matches;
84+ int no_skipped = whole_dir && REGULAR_SKIPPED(flist) == 0;
85+ const char *dirname = csum_cache[slot].dirname;
071bf6df 86+
f9df736a 87+ flist_sort_and_clean(flist, 0);
071bf6df 88+
f9df736a 89+ if (dry_run && !(checksum_files & CSF_AFFECT_DRYRUN))
071bf6df
WD
90+ return;
91+
f9df736a 92+ checksum_filename(slot, dirname, fbuf);
071bf6df 93+
f9df736a 94+ if (flist->high - flist->low < 0 && no_skipped) {
071bf6df
WD
95+ unlink(fbuf);
96+ return;
97+ }
98+
99+ if (!new_entries && (counts_match || !whole_dir))
100+ return;
101+
102+ if (!(out_fp = fopen(fbuf, "w")))
103+ return;
104+
f9df736a
WD
105+ for (i = flist->low; i <= flist->high; i++) {
106+ struct file_struct *file = flist->sorted[i];
071bf6df
WD
107+ const char *cp = F_SUM(file);
108+ const char *end = cp + checksum_len;
109+ const char *alt_sum = file->basename + strlen(file->basename) + 1;
071bf6df
WD
110+ if (whole_dir && !(file->flags & FLAG_SUM_KEEP))
111+ continue;
071bf6df
WD
112+ if (protocol_version >= 30)
113+ fprintf(out_fp, "%s ", alt_sum);
114+ if (file->flags & FLAG_SUM_MISSING) {
071bf6df 115+ do {
f9df736a 116+ fputs("==", out_fp);
071bf6df
WD
117+ } while (++cp != end);
118+ } else {
119+ do {
505968ea 120+ fprintf(out_fp, "%02x", (int)CVAL(cp, 0));
071bf6df
WD
121+ } while (++cp != end);
122+ }
123+ if (protocol_version < 30)
124+ fprintf(out_fp, " %s", alt_sum);
e2bccb59 125+ fprintf(out_fp, " %10.0f %10.0f %10lu %10lu %s\n",
071bf6df 126+ (double)F_LENGTH(file), (double)file->modtime,
f9df736a 127+ (long)F_CTIME(file), (long)F_INODE(file), file->basename);
071bf6df
WD
128+ }
129+
130+ fclose(out_fp);
071bf6df
WD
131+}
132+
f9df736a 133+void reset_checksum_cache(int whole_dir)
213d4328 134 {
f9df736a
WD
135 int slot, slots = am_sender ? 1 : basis_dir_cnt + 1;
136
fc557362 137@@ -370,6 +451,9 @@ void reset_checksum_cache()
f9df736a
WD
138 struct file_list *flist = csum_cache[slot].flist;
139
140 if (flist) {
141+ if (checksum_files & CSF_UPDATE && flist->next)
142+ write_checksums(slot, flist, whole_dir);
143+
144 /* Reset the pool memory and empty the file-list array. */
145 pool_free_old(flist->file_pool,
146 pool_boundary(flist->file_pool, 0));
fc557362 147@@ -380,6 +464,10 @@ void reset_checksum_cache()
f9df736a
WD
148 flist->low = 0;
149 flist->high = -1;
150 flist->next = NULL;
151+
152+ csum_cache[slot].checksum_matches = 0;
153+ csum_cache[slot].checksum_updates = 0;
154+ REGULAR_SKIPPED(flist) = 0;
155 }
156 }
157
fc557362 158@@ -387,7 +475,7 @@ void reset_checksum_cache()
f9df736a
WD
159 static int add_checksum(struct file_list *flist, const char *dirname,
160 const char *basename, int basename_len, OFF_T file_length,
161 time_t mtime, uint32 ctime, uint32 inode,
162- const char *sum)
163+ const char *sum, const char *alt_sum, int flags)
164 {
165 struct file_struct *file;
166 int alloc_len, extra_len;
fc557362 167@@ -404,7 +492,7 @@ static int add_checksum(struct file_list *flist, const char *dirname,
f9df736a
WD
168 if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
169 extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
170 #endif
171- alloc_len = FILE_STRUCT_LEN + extra_len + basename_len;
172+ alloc_len = FILE_STRUCT_LEN + extra_len + basename_len + checksum_len*2 + 1;
173 bp = pool_alloc(flist->file_pool, alloc_len, "add_checksum");
174
175 memset(bp, 0, extra_len + FILE_STRUCT_LEN);
fc557362 176@@ -413,7 +501,14 @@ static int add_checksum(struct file_list *flist, const char *dirname,
f9df736a
WD
177 bp += FILE_STRUCT_LEN;
178
179 memcpy(bp, basename, basename_len);
180+ if (alt_sum)
181+ strlcpy(bp+basename_len, alt_sum, checksum_len*2 + 1);
182+ else {
183+ memset(bp+basename_len, '=', checksum_len*2);
184+ bp[basename_len+checksum_len*2] = '\0';
185+ }
186
187+ file->flags = flags;
188 file->mode = S_IFREG;
189 file->modtime = mtime;
190 file->len32 = (uint32)file_length;
fc557362 191@@ -442,10 +537,11 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
213d4328 192 char line[MAXPATHLEN+1024], fbuf[MAXPATHLEN], sum[MAX_DIGEST_LEN];
213d4328 193 FILE *fp;
f9df736a
WD
194 char *cp;
195- int len, i;
196 time_t mtime;
197+ int len, i, flags;
198 OFF_T file_length;
199 uint32 ctime, inode;
200+ const char *alt_sum = NULL;
201 int dlen = dirname ? strlcpy(fbuf, dirname, sizeof fbuf) : 0;
213d4328 202
c0c7984e 203 if (dlen >= (int)(sizeof fbuf - 1 - RSYNCSUMS_LEN))
fc557362 204@@ -466,7 +562,7 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
213d4328
WD
205 while (fgets(line, sizeof line, fp)) {
206 cp = line;
207 if (protocol_version >= 30) {
208- char *alt_sum = cp;
56522462 209+ alt_sum = cp;
213d4328
WD
210 if (*cp == '=')
211 while (*++cp == '=') {}
212 else
fc557362 213@@ -477,7 +573,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
213d4328
WD
214 }
215
216 if (*cp == '=') {
217- continue;
56522462
WD
218+ for (i = 0; i < checksum_len*2; i++, cp++) {
219+ if (*cp != '=') {
ae10e51e
WD
220+ cp = "";
221+ break;
222+ }
ae10e51e 223+ }
56522462
WD
224+ memset(sum, 0, checksum_len);
225+ flags = FLAG_SUM_MISSING;
213d4328
WD
226 } else {
227 for (i = 0; i < checksum_len*2; i++, cp++) {
228 int x;
fc557362 229@@ -495,13 +598,14 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
213d4328
WD
230 else
231 sum[i/2] = x << 4;
232 }
56522462 233+ flags = 0;
213d4328
WD
234 }
235 if (*cp != ' ')
236 break;
237 while (*++cp == ' ') {}
238
239 if (protocol_version < 30) {
240- char *alt_sum = cp;
56522462 241+ alt_sum = cp;
213d4328
WD
242 if (*cp == '=')
243 while (*++cp == '=') {}
244 else
fc557362 245@@ -551,24 +655,112 @@ static void read_checksums(int slot, struct file_list *flist, const char *dirnam
213d4328
WD
246 continue;
247
248 strlcpy(fbuf+dlen, cp, sizeof fbuf - dlen);
ae10e51e 249+ if (is_excluded(fbuf, 0, ALL_FILTERS)) {
edf38a9d 250+ flags |= FLAG_SUM_KEEP;
f9df736a 251+ csum_cache[slot].checksum_matches++;
56522462 252+ }
213d4328 253
f9df736a
WD
254 add_checksum(flist, dirname, cp, len, file_length,
255 mtime, ctime, inode,
256- sum);
56522462 257+ sum, alt_sum, flags);
213d4328
WD
258 }
259 fclose(fp);
260
f9df736a
WD
261 flist_sort_and_clean(flist, CLEAN_KEEP_LAST);
262 }
263
264+void set_cached_checksum(struct file_list *file_flist, struct file_struct *file)
265+{
266+ int j;
267+ FILE *out_fp;
268+ STRUCT_STAT st;
269+ char fbuf[MAXPATHLEN];
270+ const char *fn = f_name(file, NULL);
271+ struct file_list *flist = csum_cache[0].flist;
272+
273+ if (dry_run && !(checksum_files & CSF_AFFECT_DRYRUN))
274+ return;
275+
276+ if (stat(fn, &st) < 0)
277+ return;
278+
279+ checksum_filename(0, file->dirname, fbuf);
280+
281+ if (file_flist != flist->next) {
282+ const char *cp = F_SUM(file);
283+ const char *end = cp + checksum_len;
284+
285+ if (!(out_fp = fopen(fbuf, "a")))
286+ return;
287+
288+ if (protocol_version >= 30) {
289+ for (j = 0; j < checksum_len; j++)
290+ fputs("==", out_fp);
291+ fputc(' ', out_fp);
292+ }
293+ do {
294+ fprintf(out_fp, "%02x", (int)CVAL(cp, 0));
295+ } while (++cp != end);
296+ if (protocol_version < 30) {
297+ fputc(' ', out_fp);
298+ for (j = 0; j < checksum_len; j++)
299+ fputs("==", out_fp);
300+ }
301+ fprintf(out_fp, " %10.0f %10.0f %10lu %10lu %s\n",
302+ (double)st.st_size, (double)st.st_mtime,
303+ (long)(uint32)st.st_ctime, (long)(uint32)st.st_ino,
304+ file->basename);
305+
306+ fclose(out_fp);
307+ return;
308+ }
309+
310+ if ((j = flist_find(flist, file)) >= 0) {
311+ struct file_struct *fp = flist->sorted[j];
312+ int inc = 0;
313+ if (F_LENGTH(fp) != st.st_size) {
314+ fp->len32 = (uint32)st.st_size;
315+ if (st.st_size > 0xFFFFFFFFu) {
316+ OPT_EXTRA(fp, 0)->unum = (uint32)(st.st_size >> 32);
317+ fp->flags |= FLAG_LENGTH64;
318+ } else
319+ fp->flags &= FLAG_LENGTH64;
320+ inc = 1;
321+ }
322+ if (fp->modtime != st.st_mtime) {
323+ fp->modtime = st.st_mtime;
324+ inc = 1;
325+ }
326+ if (F_CTIME(fp) != (uint32)st.st_ctime) {
327+ F_CTIME(fp) = (uint32)st.st_ctime;
328+ inc = 1;
329+ }
330+ if (F_INODE(fp) != (uint32)st.st_ino) {
331+ F_INODE(fp) = (uint32)st.st_ino;
332+ inc = 1;
333+ }
334+ memcpy(F_SUM(fp), F_SUM(file), MAX_DIGEST_LEN);
335+ csum_cache[0].checksum_updates += inc;
336+ fp->flags &= ~FLAG_SUM_MISSING;
337+ fp->flags |= FLAG_SUM_KEEP;
338+ return;
339+ }
340+
341+ csum_cache[0].checksum_updates +=
342+ add_checksum(flist, file->dirname, file->basename, strlen(file->basename) + 1,
343+ st.st_size, (uint32)st.st_mtime, (uint32)st.st_ctime,
344+ st.st_ino, F_SUM(file), NULL, FLAG_SUM_KEEP);
345+}
346+
347 void get_cached_checksum(int slot, const char *fname, struct file_struct *file,
348- STRUCT_STAT *stp, char *sum_buf)
349+ int basename_len, STRUCT_STAT *stp, char *sum_buf)
350 {
351 struct file_list *flist = csum_cache[slot].flist;
352 int j;
353
354 if (!flist->next) {
355 flist->next = cur_flist; /* next points from checksum flist to file flist */
356+ csum_cache[slot].dirname = file->dirname;
357 read_checksums(slot, flist, file->dirname);
358 }
359
fc557362 360@@ -580,12 +772,31 @@ void get_cached_checksum(int slot, const char *fname, struct file_struct *file,
f9df736a
WD
361 && (checksum_files & CSF_LAX
362 || (F_CTIME(fp) == (uint32)stp->st_ctime
363 && F_INODE(fp) == (uint32)stp->st_ino))) {
364- memcpy(sum_buf, F_SUM(fp), MAX_DIGEST_LEN);
365+ if (fp->flags & FLAG_SUM_MISSING) {
366+ fp->flags &= ~FLAG_SUM_MISSING;
367+ csum_cache[slot].checksum_updates++;
368+ file_checksum(fname, stp->st_size, sum_buf);
369+ memcpy(F_SUM(fp), sum_buf, MAX_DIGEST_LEN);
370+ } else {
371+ csum_cache[slot].checksum_matches++;
372+ memcpy(sum_buf, F_SUM(fp), MAX_DIGEST_LEN);
373+ }
374+ fp->flags |= FLAG_SUM_KEEP;
375 return;
376 }
377+ clear_file(fp);
378 }
379
380 file_checksum(fname, stp->st_size, sum_buf);
381+
382+ if (checksum_files & CSF_UPDATE) {
383+ if (basename_len < 0)
384+ basename_len = strlen(file->basename) + 1;
385+ csum_cache[slot].checksum_updates +=
386+ add_checksum(flist, file->dirname, file->basename, basename_len,
387+ stp->st_size, stp->st_mtime, (uint32)stp->st_ctime,
388+ (uint32)stp->st_ino, sum_buf, NULL, FLAG_SUM_KEEP);
389+ }
390 }
391
cbdf862c 392 /* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
fc557362 393@@ -1452,6 +1663,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
7f0bf1cb 394 if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
7200c744
WD
395 if (ignore_perishable)
396 non_perishable_cnt++;
397+ if (S_ISREG(st.st_mode))
f9df736a 398+ REGULAR_SKIPPED(flist)++;
7200c744
WD
399 return NULL;
400 }
401
fc557362 402@@ -1498,13 +1711,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
f9df736a
WD
403 lastdir[len] = '\0';
404 lastdir_len = len;
405 if (checksum_files && am_sender && flist)
406- reset_checksum_cache();
407+ reset_checksum_cache(0);
408 }
409 } else {
410 basename = thisname;
411 if (checksum_files && am_sender && flist && lastdir_len == -2) {
412 lastdir_len = -1;
413- reset_checksum_cache();
414+ reset_checksum_cache(0);
415 }
213d4328 416 }
f9df736a 417 basename_len = strlen(basename) + 1; /* count the '\0' */
fc557362 418@@ -1599,7 +1812,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
f9df736a
WD
419
420 if (always_checksum && am_sender && S_ISREG(st.st_mode)) {
421 if (flist && checksum_files)
422- get_cached_checksum(0, thisname, file, &st, tmp_sum);
423+ get_cached_checksum(0, thisname, file, basename_len, &st, tmp_sum);
424 else
425 file_checksum(thisname, st.st_size, tmp_sum);
fc557362
WD
426 if (sender_keeps_checksum)
427@@ -1971,6 +2184,9 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
ae10e51e
WD
428
429 closedir(d);
430
f9df736a
WD
431+ if (checksum_files & CSF_UPDATE && am_sender && f >= 0)
432+ reset_checksum_cache(1);
ae10e51e
WD
433+
434 if (f >= 0 && recurse && !divert_dirs) {
9c85142a
WD
435 int i, end = flist->used - 1;
436 /* send_if_directory() bumps flist->used, so use "end". */
fc557362 437@@ -2589,6 +2805,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
85096e5e 438 } else
6e9495c7 439 flist_eof = 1;
e2e42a01 440
f9df736a
WD
441+ if (checksum_files & CSF_UPDATE && flist_eof)
442+ reset_checksum_cache(0); /* writes any last updates */
e2e42a01 443+
071bf6df
WD
444 return flist;
445 }
e2e42a01 446
f9df736a 447diff --git a/generator.c b/generator.c
fc557362 448index 48a5062..8717ab7 100644
f9df736a
WD
449--- a/generator.c
450+++ b/generator.c
fc557362 451@@ -111,6 +111,7 @@ static int dir_tweaking;
c0c7984e 452 static int symlink_timeset_failed_flags;
f9df736a
WD
453 static int need_retouch_dir_times;
454 static int need_retouch_dir_perms;
455+static int started_whole_dir, upcoming_whole_dir;
456 static const char *solo_file = NULL;
457
fc557362
WD
458 enum nonregtype {
459@@ -533,7 +534,7 @@ int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st, int slot
f9df736a
WD
460 if (always_checksum > 0 && S_ISREG(st->st_mode)) {
461 char sum[MAX_DIGEST_LEN];
462 if (checksum_files && slot >= 0)
463- get_cached_checksum(slot, fn, file, st, sum);
464+ get_cached_checksum(slot, fn, file, -1, st, sum);
465 else
466 file_checksum(fn, st->st_size, sum);
467 return memcmp(sum, F_SUM(file), checksum_len) == 0;
fc557362 468@@ -1177,7 +1178,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
f9df736a
WD
469 fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, 1);
470 }
471 if (checksum_files) {
472- reset_checksum_cache();
473+ reset_checksum_cache(started_whole_dir);
474+ started_whole_dir = upcoming_whole_dir;
475 }
476 need_new_dirscan = 0;
477 }
fc557362 478@@ -1335,6 +1337,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
cbdf862c 479 else
65ecbe35 480 change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
f9df736a 481 }
65ecbe35 482+ upcoming_whole_dir = file->flags & FLAG_CONTENT_DIR && f_out != -1 ? 1 : 0;
f9df736a
WD
483 goto cleanup;
484 }
485
fc557362 486@@ -1627,6 +1630,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
f9df736a
WD
487 handle_partial_dir(partialptr, PDIR_DELETE);
488 }
489 set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
490+ if (checksum_files & CSF_UPDATE)
491+ set_cached_checksum(cur_flist, file);
492 if (itemizing)
493 itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
494 #ifdef SUPPORT_HARD_LINKS
fc557362 495@@ -2068,6 +2073,7 @@ void generate_files(int f_out, const char *local_name)
65ecbe35
WD
496 } else
497 change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
f9df736a
WD
498 }
499+ upcoming_whole_dir = fp->flags & FLAG_CONTENT_DIR ? 1 : 0;
500 }
501 for (i = cur_flist->low; i <= cur_flist->high; i++) {
502 struct file_struct *file = cur_flist->sorted[i];
fc557362 503@@ -2162,6 +2168,9 @@ void generate_files(int f_out, const char *local_name)
f9df736a
WD
504 wait_for_receiver();
505 }
506
507+ if (checksum_files)
508+ reset_checksum_cache(started_whole_dir);
509+
fc557362
WD
510 info_levels[INFO_FLIST] = save_info_flist;
511 info_levels[INFO_PROGRESS] = save_info_progress;
512
f9df736a 513diff --git a/io.c b/io.c
fc557362 514index 6a89c8f..50e73b1 100644
f9df736a
WD
515--- a/io.c
516+++ b/io.c
fc557362
WD
517@@ -49,6 +49,7 @@ extern int list_only;
518 extern int read_batch;
f9df736a
WD
519 extern int protect_args;
520 extern int checksum_seed;
521+extern int checksum_files;
522 extern int protocol_version;
523 extern int remove_source_files;
524 extern int preserve_hard_links;
963ca808 525@@ -161,6 +162,9 @@ static void got_flist_entry_status(enum festatus status, const char *buf)
f9df736a
WD
526 flist_ndx_push(&hlink_list, ndx);
527 flist->in_progress++;
528 }
529+ } else if (checksum_files & CSF_UPDATE) {
530+ struct file_struct *file = flist->files[ndx - flist->ndx_start];
531+ set_cached_checksum(flist, file);
532 }
533 break;
534 case FES_REDO:
cc3e685d 535diff --git a/loadparm.c b/loadparm.c
fc557362 536index 899d2b5..3123c93 100644
cc3e685d
WD
537--- a/loadparm.c
538+++ b/loadparm.c
fc557362 539@@ -311,6 +311,10 @@ static struct enum_list enum_csum_modes[] = {
f9df736a
WD
540 { CSF_IGNORE_FILES, "none" },
541 { CSF_LAX_MODE, "lax" },
542 { CSF_STRICT_MODE, "strict" },
543+ { CSF_LAX_MODE|CSF_UPDATE, "+lax" },
544+ { CSF_STRICT_MODE|CSF_UPDATE, "+strict" },
545+ { CSF_LAX_MODE|CSF_UPDATE|CSF_AFFECT_DRYRUN, "++lax" },
546+ { CSF_STRICT_MODE|CSF_UPDATE|CSF_AFFECT_DRYRUN, "++strict" },
547 { -1, NULL }
548 };
549
cc3e685d 550diff --git a/options.c b/options.c
fc557362 551index 2e110f3..26d5561 100644
cc3e685d
WD
552--- a/options.c
553+++ b/options.c
fc557362 554@@ -1635,7 +1635,15 @@ int parse_arguments(int *argc_p, const char ***argv_p)
f9df736a
WD
555
556 case OPT_SUMFILES:
557 arg = poptGetOptArg(pc);
558- checksum_files = 0;
559+ if (*arg == '+') {
560+ arg++;
561+ checksum_files = CSF_UPDATE;
562+ if (*arg == '+') {
563+ arg++;
564+ checksum_files |= CSF_AFFECT_DRYRUN;
565+ }
566+ } else
567+ checksum_files = 0;
568 if (strcmp(arg, "lax") == 0)
569 checksum_files |= CSF_LAX_MODE;
570 else if (strcmp(arg, "strict") == 0)
571diff --git a/receiver.c b/receiver.c
fc557362 572index 4325e30..2cea8fe 100644
f9df736a
WD
573--- a/receiver.c
574+++ b/receiver.c
fc557362 575@@ -47,6 +47,7 @@ extern int sparse_files;
f9df736a 576 extern int keep_partial;
fc557362 577 extern int checksum_len;
f9df736a
WD
578 extern int checksum_seed;
579+extern int checksum_files;
580 extern int inplace;
581 extern int delay_updates;
582 extern mode_t orig_umask;
fc557362 583@@ -344,7 +345,7 @@ static void handle_delayed_updates(char *local_name)
f9df736a
WD
584 "rename failed for %s (from %s)",
585 full_fname(fname), partialptr);
586 } else {
587- if (remove_source_files
588+ if (remove_source_files || checksum_files & CSF_UPDATE
589 || (preserve_hard_links && F_IS_HLINKED(file)))
590 send_msg_int(MSG_SUCCESS, ndx);
591 handle_partial_dir(partialptr, PDIR_DELETE);
fc557362 592@@ -794,7 +795,7 @@ int recv_files(int f_in, char *local_name)
ae306a29
WD
593 case 2:
594 break;
f9df736a
WD
595 case 1:
596- if (remove_source_files || inc_recurse
597+ if (remove_source_files || inc_recurse || checksum_files & CSF_UPDATE
598 || (preserve_hard_links && F_IS_HLINKED(file)))
599 send_msg_int(MSG_SUCCESS, ndx);
600 break;
cc3e685d 601diff --git a/rsync.h b/rsync.h
fc557362 602index ba8f3db..89c47bd 100644
cc3e685d
WD
603--- a/rsync.h
604+++ b/rsync.h
fc557362 605@@ -908,6 +908,8 @@ typedef struct {
7200c744 606
f9df736a
WD
607 #define CSF_ENABLE (1<<1)
608 #define CSF_LAX (1<<2)
609+#define CSF_UPDATE (1<<3)
610+#define CSF_AFFECT_DRYRUN (1<<4)
611
612 #define CSF_IGNORE_FILES 0
613 #define CSF_LAX_MODE (CSF_ENABLE|CSF_LAX)
cc3e685d 614diff --git a/rsync.yo b/rsync.yo
fc557362 615index 7aa62cf..a119ed7 100644
cc3e685d
WD
616--- a/rsync.yo
617+++ b/rsync.yo
fc557362 618@@ -596,9 +596,13 @@ computed just as it would be if bf(--sumfiles) was not specified.
f9df736a
WD
619
620 The MODE value is either "lax", for relaxed checking (which compares size
621 and mtime), "strict" (which also compares ctime and inode), or "none" to
622-ignore any .rsyncsums files ("none" is the default). Rsync does not create
623-or update these files, but there is a perl script in the support directory
624-named "rsyncsums" that can be used for that.
625+ignore any .rsyncsums files ("none" is the default).
626+If you want rsync to create and/or update these files, specify a prefixed
627+plus ("+lax" or "+strict").
628+Adding a second prefixed '+' causes the checksum-file updates to happen
629+even when the transfer is in bf(--dry-run) mode ("++lax" or "++strict").
630+There is also a perl script in the support directory named "rsyncsums"
631+that can be used to update the .rsyncsums files.
632
633 This option has no effect unless bf(--checksum, -c) was also specified. It
634 also only affects the current side of the transfer, so if you want the
cc3e685d 635diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo
fc557362 636index 0fc98fd..3024842 100644
cc3e685d
WD
637--- a/rsyncd.conf.yo
638+++ b/rsyncd.conf.yo
fc557362 639@@ -295,13 +295,15 @@ The default is tt(/var/run/rsyncd.lock).
e66d6d51 640 dit(bf(checksum files)) This parameter tells rsync to make use of any cached
f9df736a
WD
641 checksum information it finds in per-directory .rsyncsums files when the
642 current transfer is using the bf(--checksum) option. The value can be set
643-to either "lax", "strict", or "none" -- see the client's bf(--sumfiles)
644-option for what these choices do.
645+to either "lax", "strict", "+lax", "+strict", "++lax", "++strict", or
646+"none". See the client's bf(--sumfiles) option for what these choices do.
647
648 Note also that the client's command-line option, bf(--sumfiles), has no
649 effect on a daemon. A daemon will only access checksum files if this
650-config option tells it to. See also the bf(exclude) directive for a way
651-to hide the .rsyncsums files from the user.
652+config option tells it to. You can configure updating of the .rsyncsums
653+files even if the module itself is configured to be read-only. See also
654+the bf(exclude) directive for a way to hide the .rsyncsums files from the
655+user.
656
e66d6d51 657 dit(bf(read only)) This parameter determines whether clients
ae10e51e 658 will be able to upload files or not. If "read only" is true then any