diff --git a/checksum.c b/checksum.c index 24e46bfbb..67b15a78d 100644 --- a/checksum.c +++ b/checksum.c @@ -37,6 +37,7 @@ extern int am_server; extern int whole_file; +extern int allowed_lull; extern int checksum_seed; extern int protocol_version; extern int proper_seed_order; @@ -398,6 +399,41 @@ void get_checksum2(char *buf, int32 len, char *sum) } } +/* Hashing a large file sends nothing to the peer for as long as it takes, so a + * --checksum run over a big file looks like a dead link and the far end aborts + * once --timeout elapses. Tick the keep-alive as we go; maybe_send_keepalive() + * itself only acts once allowed_lull (half of --timeout) has passed. + * + * `n` is the size of the piece just hashed, and we only consult the clock once + * per CHUNK_SIZE of it. The MD4 loop feeds us CSUM_CHUNK (64) bytes at a time, + * so ticking per piece would call time() 512x more often for MD4 than for the + * other digests -- 16.7 million times per GiB, which is cheap on a vDSO clock + * and decidedly not on a platform where time() is a real syscall. */ +static void checksum_keepalive(int32 n) +{ + static int32 until_tick = CHUNK_SIZE; + + if (!allowed_lull) + return; + if ((until_tick -= n) > 0) + return; + until_tick = CHUNK_SIZE; + maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); +} + +/* Feed the mapped file to a digest in `chunk`-sized pieces. UPDATE is an + * expression using _p/_n for the piece; the digests have differing signatures, + * so an expression is what lets one loop serve all of them. Each caller keeps + * its own trailing-partial-chunk handling, which is not identical across + * digests (see the pre-27 MD4 case below). */ +#define CHECKSUM_FEED(chunk, UPDATE) \ + for (i = 0; i + (chunk) <= len; i += (chunk)) { \ + const char *_p = map_ptr(buf, i, (chunk)); \ + int32 _n = (chunk); \ + UPDATE; \ + checksum_keepalive(_n); \ + } + void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) { struct map_struct *buf; @@ -421,8 +457,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) EVP_DigestInit_ex(evp, file_sum_evp_md, NULL); - for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) - EVP_DigestUpdate(evp, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE); + CHECKSUM_FEED(CHUNK_SIZE, EVP_DigestUpdate(evp, (uchar *)_p, _n)); remainder = (int32)(len - i); if (remainder > 0) @@ -440,8 +475,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) XXH64_reset(state, 0); - for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) - XXH64_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE); + CHECKSUM_FEED(CHUNK_SIZE, XXH64_update(state, (uchar *)_p, _n)); remainder = (int32)(len - i); if (remainder > 0) @@ -459,8 +493,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) XXH3_64bits_reset(state); - for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) - XXH3_64bits_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE); + CHECKSUM_FEED(CHUNK_SIZE, XXH3_64bits_update(state, (uchar *)_p, _n)); remainder = (int32)(len - i); if (remainder > 0) @@ -477,8 +510,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) XXH3_128bits_reset(state); - for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) - XXH3_128bits_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE); + CHECKSUM_FEED(CHUNK_SIZE, XXH3_128bits_update(state, (uchar *)_p, _n)); remainder = (int32)(len - i); if (remainder > 0) @@ -495,8 +527,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) md5_begin(&m5); - for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) - md5_update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE); + CHECKSUM_FEED(CHUNK_SIZE, md5_update(&m5, (uchar *)_p, _n)); remainder = (int32)(len - i); if (remainder > 0) @@ -513,8 +544,7 @@ void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum) mdfour_begin(&m); - for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK) - mdfour_update(&m, (uchar *)map_ptr(buf, i, CSUM_CHUNK), CSUM_CHUNK); + CHECKSUM_FEED(CSUM_CHUNK, mdfour_update(&m, (uchar *)_p, _n)); /* Prior to version 27 an incorrect MD4 checksum was computed * by failing to call mdfour_tail() for block sizes that