Improve performance of file_checksum()
[rsync.git] / checksum.c
1 /*
2  * Routines to support checksumming of bytes.
3  *
4  * Copyright (C) 1996 Andrew Tridgell
5  * Copyright (C) 1996 Paul Mackerras
6  * Copyright (C) 2004-2020 Wayne Davison
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * In addition, as a special exception, the copyright holders give
14  * permission to dynamically link rsync with the OpenSSL and xxhash
15  * libraries when those libraries are being distributed in compliance
16  * with their license terms, and to distribute a dynamically linked
17  * combination of rsync and these libraries.  This is also considered
18  * to be covered under the GPL's System Libraries exception.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License along
26  * with this program; if not, visit the http://fsf.org website.
27  */
28
29 #include "rsync.h"
30 #ifdef SUPPORT_XXHASH
31 #include "xxhash.h"
32 #endif
33 #ifdef USE_OPENSSL
34 #include "openssl/md4.h"
35 #include "openssl/md5.h"
36 #endif
37
38 extern int am_server;
39 extern int local_server;
40 extern int whole_file;
41 extern int read_batch;
42 extern int checksum_seed;
43 extern int protocol_version;
44 extern int proper_seed_order;
45 extern char *checksum_choice;
46
47 #define CSUM_NONE 0
48 #define CSUM_MD4_ARCHAIC 1
49 #define CSUM_MD4_BUSTED 2
50 #define CSUM_MD4_OLD 3
51 #define CSUM_MD4 4
52 #define CSUM_MD5 5
53 #define CSUM_XXH64 6
54
55 struct name_num_obj valid_checksums = {
56         "checksum", NULL, NULL, 0, 0, {
57 #ifdef SUPPORT_XXHASH
58                 { CSUM_XXH64, "xxh64", NULL },
59                 { CSUM_XXH64, "xxhash", NULL },
60 #endif
61                 { CSUM_MD5, "md5", NULL },
62                 { CSUM_MD4, "md4", NULL },
63                 { CSUM_NONE, "none", NULL },
64                 { 0, NULL, NULL }
65         }
66 };
67
68 #ifndef USE_OPENSSL
69 #define MD5_CTX md_context
70 #define MD5_Init md5_begin
71 #define MD5_Update md5_update
72 #define MD5_Final(digest, cptr) md5_result(cptr, digest)
73 #endif
74
75 int xfersum_type = 0; /* used for the file transfer checksums */
76 int checksum_type = 0; /* used for the pre-transfer (--checksum) checksums */
77
78 static int parse_csum_name(const char *name, int len)
79 {
80         struct name_num_item *nni;
81
82         if (len < 0 && name)
83                 len = strlen(name);
84
85         if (!name || (len == 4 && strncasecmp(name, "auto", 4) == 0)) {
86                 if (protocol_version >= 30)
87                         return CSUM_MD5;
88                 if (protocol_version >= 27)
89                         return CSUM_MD4_OLD;
90                 if (protocol_version >= 21)
91                         return CSUM_MD4_BUSTED;
92                 return CSUM_MD4_ARCHAIC;
93         }
94
95         nni = get_nni_by_name(&valid_checksums, name, len);
96
97         if (!nni) {
98                 rprintf(FERROR, "unknown checksum name: %s\n", name);
99                 exit_cleanup(RERR_UNSUPPORTED);
100         }
101
102         return nni->num;
103 }
104
105 static const char *checksum_name(int num)
106 {
107         struct name_num_item *nni = get_nni_by_num(&valid_checksums, num);
108
109         return nni ? nni->name : num < CSUM_MD4 ? "MD4" : "UNKNOWN";
110 }
111
112 void parse_checksum_choice(int final_call)
113 {
114         if (!valid_checksums.negotiated_name) {
115                 char *cp = checksum_choice ? strchr(checksum_choice, ',') : NULL;
116                 if (cp) {
117                         xfersum_type = parse_csum_name(checksum_choice, cp - checksum_choice);
118                         checksum_type = parse_csum_name(cp+1, -1);
119                 } else
120                         xfersum_type = checksum_type = parse_csum_name(checksum_choice, -1);
121         }
122
123         if (xfersum_type == CSUM_NONE)
124                 whole_file = 1;
125
126         if (final_call && DEBUG_GTE(NSTR, am_server ? 2 : 1)) {
127                 const char *c_s = am_server ? "Server" : "Client";
128                 if (valid_checksums.negotiated_name)
129                         rprintf(FINFO, "%s negotiated checksum: %s\n", c_s, valid_checksums.negotiated_name);
130                 else if (xfersum_type == checksum_type) {
131                         rprintf(FINFO, "%s %s checksum: %s\n", c_s,
132                                 checksum_choice ? "chosen" : "protocol-based",
133                                 checksum_name(xfersum_type));
134                 } else {
135                         rprintf(FINFO, "%s chosen transfer checksum: %s\n",
136                                 c_s, checksum_name(xfersum_type));
137                         rprintf(FINFO, "%s chosen pre-transfer checksum: %s\n",
138                                 c_s, checksum_name(checksum_type));
139                 }
140         }
141 }
142
143 int csum_len_for_type(int cst, BOOL flist_csum)
144 {
145         switch (cst) {
146           case CSUM_NONE:
147                 return 1;
148           case CSUM_MD4_ARCHAIC:
149                 /* The oldest checksum code is rather weird: the file-list code only sent
150                  * 2-byte checksums, but all other checksums were full MD4 length. */
151                 return flist_csum ? 2 : MD4_DIGEST_LEN;
152           case CSUM_MD4:
153           case CSUM_MD4_OLD:
154           case CSUM_MD4_BUSTED:
155                 return MD4_DIGEST_LEN;
156           case CSUM_MD5:
157                 return MD5_DIGEST_LEN;
158 #ifdef SUPPORT_XXHASH
159           case CSUM_XXH64:
160                 return 64/8;
161 #endif
162           default: /* paranoia to prevent missing case values */
163                 exit_cleanup(RERR_UNSUPPORTED);
164         }
165         return 0;
166 }
167
168 int canonical_checksum(int csum_type)
169 {
170         return csum_type >= CSUM_MD4 ? 1 : 0;
171 }
172
173 #ifndef HAVE_SIMD /* See simd-checksum-*.cpp. */
174 /*
175   a simple 32 bit checksum that can be updated from either end
176   (inspired by Mark Adler's Adler-32 checksum)
177   */
178 uint32 get_checksum1(char *buf1, int32 len)
179 {
180         int32 i;
181         uint32 s1, s2;
182         schar *buf = (schar *)buf1;
183
184         s1 = s2 = 0;
185         for (i = 0; i < (len-4); i+=4) {
186                 s2 += 4*(s1 + buf[i]) + 3*buf[i+1] + 2*buf[i+2] + buf[i+3] + 10*CHAR_OFFSET;
187                 s1 += (buf[i+0] + buf[i+1] + buf[i+2] + buf[i+3] + 4*CHAR_OFFSET);
188         }
189         for (; i < len; i++) {
190                 s1 += (buf[i]+CHAR_OFFSET); s2 += s1;
191         }
192         return (s1 & 0xffff) + (s2 << 16);
193 }
194 #endif
195
196 void get_checksum2(char *buf, int32 len, char *sum)
197 {
198         switch (xfersum_type) {
199           case CSUM_MD5: {
200                 MD5_CTX m5;
201                 uchar seedbuf[4];
202                 MD5_Init(&m5);
203                 if (proper_seed_order) {
204                         if (checksum_seed) {
205                                 SIVALu(seedbuf, 0, checksum_seed);
206                                 MD5_Update(&m5, seedbuf, 4);
207                         }
208                         MD5_Update(&m5, (uchar *)buf, len);
209                 } else {
210                         MD5_Update(&m5, (uchar *)buf, len);
211                         if (checksum_seed) {
212                                 SIVALu(seedbuf, 0, checksum_seed);
213                                 MD5_Update(&m5, seedbuf, 4);
214                         }
215                 }
216                 MD5_Final((uchar *)sum, &m5);
217                 break;
218           }
219           case CSUM_MD4:
220 #ifdef USE_OPENSSL
221           {
222                 MD4_CTX m4;
223                 MD4_Init(&m4);
224                 MD4_Update(&m4, (uchar *)buf, len);
225                 if (checksum_seed) {
226                         uchar seedbuf[4];
227                         SIVALu(seedbuf, 0, checksum_seed);
228                         MD4_Update(&m4, seedbuf, 4);
229                 }
230                 MD4_Final((uchar *)sum, &m4);
231                 break;
232           }
233 #endif
234           case CSUM_MD4_OLD:
235           case CSUM_MD4_BUSTED:
236           case CSUM_MD4_ARCHAIC: {
237                 md_context m;
238                 int32 i;
239                 static char *buf1;
240                 static int32 len1;
241
242                 mdfour_begin(&m);
243
244                 if (len > len1) {
245                         if (buf1)
246                                 free(buf1);
247                         buf1 = new_array(char, len+4);
248                         len1 = len;
249                         if (!buf1)
250                                 out_of_memory("get_checksum2");
251                 }
252
253                 memcpy(buf1, buf, len);
254                 if (checksum_seed) {
255                         SIVAL(buf1,len,checksum_seed);
256                         len += 4;
257                 }
258
259                 for (i = 0; i + CSUM_CHUNK <= len; i += CSUM_CHUNK)
260                         mdfour_update(&m, (uchar *)(buf1+i), CSUM_CHUNK);
261
262                 /*
263                  * Prior to version 27 an incorrect MD4 checksum was computed
264                  * by failing to call mdfour_tail() for block sizes that
265                  * are multiples of 64.  This is fixed by calling mdfour_update()
266                  * even when there are no more bytes.
267                  */
268                 if (len - i > 0 || xfersum_type > CSUM_MD4_BUSTED)
269                         mdfour_update(&m, (uchar *)(buf1+i), len-i);
270
271                 mdfour_result(&m, (uchar *)sum);
272                 break;
273           }
274 #ifdef SUPPORT_XXHASH
275           case CSUM_XXH64:
276                 SIVAL64(sum, 0, XXH64(buf, len, checksum_seed));
277                 break;
278 #endif
279           default: /* paranoia to prevent missing case values */
280                 exit_cleanup(RERR_UNSUPPORTED);
281         }
282 }
283
284 void file_checksum(const char *fname, const STRUCT_STAT *st_p, char *sum)
285 {
286         struct map_struct *buf;
287         OFF_T i, len = st_p->st_size;
288         int32 remainder;
289         int fd;
290
291         memset(sum, 0, MAX_DIGEST_LEN);
292
293         fd = do_open(fname, O_RDONLY, 0);
294         if (fd == -1)
295                 return;
296
297         buf = map_file(fd, len, MAX_MAP_SIZE, CHUNK_SIZE);
298
299         switch (checksum_type) {
300           case CSUM_MD5: {
301                 MD5_CTX m5;
302
303                 MD5_Init(&m5);
304
305                 for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
306                         MD5_Update(&m5, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
307
308                 remainder = (int32)(len - i);
309                 if (remainder > 0)
310                         MD5_Update(&m5, (uchar *)map_ptr(buf, i, remainder), remainder);
311
312                 MD5_Final((uchar *)sum, &m5);
313                 break;
314           }
315           case CSUM_MD4:
316 #ifdef USE_OPENSSL
317           {
318                 MD4_CTX m4;
319
320                 MD4_Init(&m4);
321
322                 for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
323                         MD4_Update(&m4, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
324
325                 remainder = (int32)(len - i);
326                 if (remainder > 0)
327                         MD4_Update(&m4, (uchar *)map_ptr(buf, i, remainder), remainder);
328
329                 MD4_Final((uchar *)sum, &m4);
330                 break;
331           }
332 #endif
333           case CSUM_MD4_OLD:
334           case CSUM_MD4_BUSTED:
335           case CSUM_MD4_ARCHAIC: {
336                 md_context m;
337
338                 mdfour_begin(&m);
339
340                 for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE)
341                         mdfour_update(&m, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
342
343                 /* Prior to version 27 an incorrect MD4 checksum was computed
344                  * by failing to call mdfour_tail() for block sizes that
345                  * are multiples of 64.  This is fixed by calling mdfour_update()
346                  * even when there are no more bytes. */
347                 remainder = (int32)(len - i);
348                 if (remainder > 0 || checksum_type > CSUM_MD4_BUSTED)
349                         mdfour_update(&m, (uchar *)map_ptr(buf, i, remainder), remainder);
350
351                 mdfour_result(&m, (uchar *)sum);
352                 break;
353           }
354 #ifdef SUPPORT_XXHASH
355           case CSUM_XXH64: {
356                 XXH64_state_t* state = XXH64_createState();
357                 if (state == NULL)
358                         out_of_memory("file_checksum XXH64");
359
360                 if (XXH64_reset(state, 0) == XXH_ERROR) {
361                         rprintf(FERROR, "error resetting XXH64 seed");
362                         exit_cleanup(RERR_STREAMIO);
363                 }
364
365                 for (i = 0; i + CHUNK_SIZE <= len; i += CHUNK_SIZE) {
366                         XXH_errorcode const updateResult =
367                             XXH64_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), CHUNK_SIZE);
368                         if (updateResult == XXH_ERROR) {
369                                 rprintf(FERROR, "error computing XXH64 hash");
370                                 exit_cleanup(RERR_STREAMIO);
371                         }
372                 }
373
374                 remainder = (int32)(len - i);
375                 if (remainder > 0)
376                         XXH64_update(state, (uchar *)map_ptr(buf, i, CHUNK_SIZE), remainder);
377                 SIVAL64(sum, 0, XXH64_digest(state));
378
379                 XXH64_freeState(state);
380                 break;
381           }
382 #endif
383           default:
384                 rprintf(FERROR, "Invalid checksum-choice for --checksum: %s (%d)\n",
385                         checksum_name(checksum_type), checksum_type);
386                 exit_cleanup(RERR_UNSUPPORTED);
387         }
388
389         close(fd);
390         unmap_file(buf);
391 }
392
393 static int32 sumresidue;
394 static union {
395         md_context md;
396 #ifdef USE_OPENSSL
397         MD4_CTX m4;
398 #endif
399         MD5_CTX m5;
400 } ctx;
401 #ifdef SUPPORT_XXHASH
402 static XXH64_state_t* xxh64_state;
403 #endif
404 static int cursum_type;
405
406 void sum_init(int csum_type, int seed)
407 {
408         char s[4];
409
410         if (csum_type < 0)
411                 csum_type = parse_csum_name(NULL, 0);
412         cursum_type = csum_type;
413
414         switch (csum_type) {
415           case CSUM_MD5:
416                 MD5_Init(&ctx.m5);
417                 break;
418           case CSUM_MD4:
419 #ifdef USE_OPENSSL
420                 MD4_Init(&ctx.m4);
421 #else
422                 mdfour_begin(&ctx.md);
423                 sumresidue = 0;
424 #endif
425                 break;
426           case CSUM_MD4_OLD:
427           case CSUM_MD4_BUSTED:
428           case CSUM_MD4_ARCHAIC:
429                 mdfour_begin(&ctx.md);
430                 sumresidue = 0;
431                 SIVAL(s, 0, seed);
432                 sum_update(s, 4);
433                 break;
434 #ifdef SUPPORT_XXHASH
435           case CSUM_XXH64:
436                 if (xxh64_state == NULL) {
437                         xxh64_state = XXH64_createState();
438                         if (xxh64_state == NULL)
439                                 out_of_memory("sum_init xxh64");
440                 }
441                 if (XXH64_reset(xxh64_state, 0) == XXH_ERROR) {
442                         rprintf(FERROR, "error resetting XXH64 state");
443                         exit_cleanup(RERR_STREAMIO);
444                 }
445                 break;
446 #endif
447           case CSUM_NONE:
448                 break;
449           default: /* paranoia to prevent missing case values */
450                 exit_cleanup(RERR_UNSUPPORTED);
451         }
452 }
453
454 /**
455  * Feed data into an MD4 accumulator, md.  The results may be
456  * retrieved using sum_end().  md is used for different purposes at
457  * different points during execution.
458  *
459  * @todo Perhaps get rid of md and just pass in the address each time.
460  * Very slightly clearer and slower.
461  **/
462 void sum_update(const char *p, int32 len)
463 {
464         switch (cursum_type) {
465           case CSUM_MD5:
466                 MD5_Update(&ctx.m5, (uchar *)p, len);
467                 break;
468           case CSUM_MD4:
469 #ifdef USE_OPENSSL
470                 MD4_Update(&ctx.m4, (uchar *)p, len);
471                 break;
472 #endif
473           case CSUM_MD4_OLD:
474           case CSUM_MD4_BUSTED:
475           case CSUM_MD4_ARCHAIC:
476                 if (len + sumresidue < CSUM_CHUNK) {
477                         memcpy(ctx.md.buffer + sumresidue, p, len);
478                         sumresidue += len;
479                         break;
480                 }
481
482                 if (sumresidue) {
483                         int32 i = CSUM_CHUNK - sumresidue;
484                         memcpy(ctx.md.buffer + sumresidue, p, i);
485                         mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, CSUM_CHUNK);
486                         len -= i;
487                         p += i;
488                 }
489
490                 while (len >= CSUM_CHUNK) {
491                         mdfour_update(&ctx.md, (uchar *)p, CSUM_CHUNK);
492                         len -= CSUM_CHUNK;
493                         p += CSUM_CHUNK;
494                 }
495
496                 sumresidue = len;
497                 if (sumresidue)
498                         memcpy(ctx.md.buffer, p, sumresidue);
499                 break;
500 #ifdef SUPPORT_XXHASH
501           case CSUM_XXH64:
502                 if (XXH64_update(xxh64_state, p, len) == XXH_ERROR) {
503                         rprintf(FERROR, "error computing XXH64 hash");
504                         exit_cleanup(RERR_STREAMIO);
505                 }
506                 break;
507 #endif
508           case CSUM_NONE:
509                 break;
510           default: /* paranoia to prevent missing case values */
511                 exit_cleanup(RERR_UNSUPPORTED);
512         }
513 }
514
515 /* NOTE: all the callers of sum_end() pass in a pointer to a buffer that is
516  * MAX_DIGEST_LEN in size, so even if the csum-len is shorter that that (i.e.
517  * CSUM_MD4_ARCHAIC), we don't have to worry about limiting the data we write
518  * into the "sum" buffer. */
519 int sum_end(char *sum)
520 {
521         switch (cursum_type) {
522           case CSUM_MD5:
523                 MD5_Final((uchar *)sum, &ctx.m5);
524                 break;
525           case CSUM_MD4:
526 #ifdef USE_OPENSSL
527                 MD4_Final((uchar *)sum, &ctx.m4);
528                 break;
529 #endif
530           case CSUM_MD4_OLD:
531                 mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, sumresidue);
532                 mdfour_result(&ctx.md, (uchar *)sum);
533                 break;
534           case CSUM_MD4_BUSTED:
535           case CSUM_MD4_ARCHAIC:
536                 if (sumresidue)
537                         mdfour_update(&ctx.md, (uchar *)ctx.md.buffer, sumresidue);
538                 mdfour_result(&ctx.md, (uchar *)sum);
539                 break;
540 #ifdef SUPPORT_XXHASH
541           case CSUM_XXH64:
542                 SIVAL64(sum, 0, XXH64_digest(xxh64_state));
543                 break;
544 #endif
545           case CSUM_NONE:
546                 *sum = '\0';
547                 break;
548           default: /* paranoia to prevent missing case values */
549                 exit_cleanup(RERR_UNSUPPORTED);
550         }
551
552         return csum_len_for_type(cursum_type, 0);
553 }