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