]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/checksum.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_subr / checksum.c
1 /*
2  * checksum.c:   checksum routines
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #define APR_WANT_BYTEFUNC
25
26 #include <ctype.h>
27
28 #include <apr_md5.h>
29 #include <apr_sha1.h>
30
31 #include "svn_checksum.h"
32 #include "svn_error.h"
33 #include "svn_ctype.h"
34 #include "svn_sorts.h"
35
36 #include "checksum.h"
37 #include "fnv1a.h"
38
39 #include "private/svn_subr_private.h"
40
41 #include "svn_private_config.h"
42
43 \f
44
45 /* The MD5 digest for the empty string. */
46 static const unsigned char md5_empty_string_digest_array[] = {
47   0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
48   0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
49 };
50
51 /* The SHA1 digest for the empty string. */
52 static const unsigned char sha1_empty_string_digest_array[] = {
53   0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
54   0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
55 };
56
57 /* The FNV-1a digest for the empty string. */
58 static const unsigned char fnv1a_32_empty_string_digest_array[] = {
59   0x81, 0x1c, 0x9d, 0xc5
60 };
61
62 /* The FNV-1a digest for the empty string. */
63 static const unsigned char fnv1a_32x4_empty_string_digest_array[] = {
64   0xcd, 0x6d, 0x9a, 0x85
65 };
66
67 /* Digests for an empty string, indexed by checksum type */
68 static const unsigned char * empty_string_digests[] = {
69   md5_empty_string_digest_array,
70   sha1_empty_string_digest_array,
71   fnv1a_32_empty_string_digest_array,
72   fnv1a_32x4_empty_string_digest_array
73 };
74
75 /* Digest sizes in bytes, indexed by checksum type */
76 static const apr_size_t digest_sizes[] = {
77   APR_MD5_DIGESTSIZE,
78   APR_SHA1_DIGESTSIZE,
79   sizeof(apr_uint32_t),
80   sizeof(apr_uint32_t)
81 };
82
83 /* Checksum type prefixes used in serialized checksums. */
84 static const char *ckind_str[] = {
85   "$md5 $",
86   "$sha1$",
87   "$fnv1$",
88   "$fnvm$",
89 };
90
91 /* Returns the digest size of it's argument. */
92 #define DIGESTSIZE(k) \
93   (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k])
94
95 /* Largest supported digest size */
96 #define MAX_DIGESTSIZE (MAX(APR_MD5_DIGESTSIZE,APR_SHA1_DIGESTSIZE))
97
98 const unsigned char *
99 svn__empty_string_digest(svn_checksum_kind_t kind)
100 {
101   return empty_string_digests[kind];
102 }
103
104 const char *
105 svn__digest_to_cstring_display(const unsigned char digest[],
106                                apr_size_t digest_size,
107                                apr_pool_t *pool)
108 {
109   static const char *hex = "0123456789abcdef";
110   char *str = apr_palloc(pool, (digest_size * 2) + 1);
111   apr_size_t i;
112
113   for (i = 0; i < digest_size; i++)
114     {
115       str[i*2]   = hex[digest[i] >> 4];
116       str[i*2+1] = hex[digest[i] & 0x0f];
117     }
118   str[i*2] = '\0';
119
120   return str;
121 }
122
123
124 const char *
125 svn__digest_to_cstring(const unsigned char digest[],
126                        apr_size_t digest_size,
127                        apr_pool_t *pool)
128 {
129   static const unsigned char zeros_digest[MAX_DIGESTSIZE] = { 0 };
130
131   if (memcmp(digest, zeros_digest, digest_size) != 0)
132     return svn__digest_to_cstring_display(digest, digest_size, pool);
133   else
134     return NULL;
135 }
136
137
138 svn_boolean_t
139 svn__digests_match(const unsigned char d1[],
140                    const unsigned char d2[],
141                    apr_size_t digest_size)
142 {
143   static const unsigned char zeros[MAX_DIGESTSIZE] = { 0 };
144
145   return ((memcmp(d1, d2, digest_size) == 0)
146           || (memcmp(d2, zeros, digest_size) == 0)
147           || (memcmp(d1, zeros, digest_size) == 0));
148 }
149
150 /* Check to see if KIND is something we recognize.  If not, return
151  * SVN_ERR_BAD_CHECKSUM_KIND */
152 static svn_error_t *
153 validate_kind(svn_checksum_kind_t kind)
154 {
155   if (kind >= svn_checksum_md5 && kind <= svn_checksum_fnv1a_32x4)
156     return SVN_NO_ERROR;
157   else
158     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
159 }
160
161 /* Create a svn_checksum_t with everything but the contents of the
162    digest populated. */
163 static svn_checksum_t *
164 checksum_create_without_digest(svn_checksum_kind_t kind,
165                                apr_size_t digest_size,
166                                apr_pool_t *pool)
167 {
168   /* Use apr_palloc() instead of apr_pcalloc() so that the digest
169    * contents are only set once by the caller. */
170   svn_checksum_t *checksum = apr_palloc(pool, sizeof(*checksum) + digest_size);
171   checksum->digest = (unsigned char *)checksum + sizeof(*checksum);
172   checksum->kind = kind;
173   return checksum;
174 }
175
176 /* Return a checksum object, allocated in POOL.  The checksum will be of
177  * type KIND and contain the given DIGEST.
178  */
179 static svn_checksum_t *
180 checksum_create(svn_checksum_kind_t kind,
181                 const unsigned char *digest,
182                 apr_pool_t *pool)
183 {
184   apr_size_t digest_size = DIGESTSIZE(kind);
185   svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size,
186                                                             pool);
187   memcpy((unsigned char *)checksum->digest, digest, digest_size);
188   return checksum;
189 }
190
191 svn_checksum_t *
192 svn_checksum_create(svn_checksum_kind_t kind,
193                     apr_pool_t *pool)
194 {
195   svn_checksum_t *checksum;
196   apr_size_t digest_size;
197
198   switch (kind)
199     {
200       case svn_checksum_md5:
201       case svn_checksum_sha1:
202       case svn_checksum_fnv1a_32:
203       case svn_checksum_fnv1a_32x4:
204         digest_size = digest_sizes[kind];
205         break;
206
207       default:
208         return NULL;
209     }
210
211   checksum = checksum_create_without_digest(kind, digest_size, pool);
212   memset((unsigned char *) checksum->digest, 0, digest_size);
213   return checksum;
214 }
215
216 svn_checksum_t *
217 svn_checksum__from_digest_md5(const unsigned char *digest,
218                               apr_pool_t *result_pool)
219 {
220   return checksum_create(svn_checksum_md5, digest, result_pool);
221 }
222
223 svn_checksum_t *
224 svn_checksum__from_digest_sha1(const unsigned char *digest,
225                                apr_pool_t *result_pool)
226 {
227   return checksum_create(svn_checksum_sha1, digest, result_pool);
228 }
229
230 svn_checksum_t *
231 svn_checksum__from_digest_fnv1a_32(const unsigned char *digest,
232                                    apr_pool_t *result_pool)
233 {
234   return checksum_create(svn_checksum_fnv1a_32, digest, result_pool);
235 }
236
237 svn_checksum_t *
238 svn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest,
239                                      apr_pool_t *result_pool)
240 {
241   return checksum_create(svn_checksum_fnv1a_32x4, digest, result_pool);
242 }
243
244 svn_error_t *
245 svn_checksum_clear(svn_checksum_t *checksum)
246 {
247   SVN_ERR(validate_kind(checksum->kind));
248
249   memset((unsigned char *) checksum->digest, 0, DIGESTSIZE(checksum->kind));
250   return SVN_NO_ERROR;
251 }
252
253 svn_boolean_t
254 svn_checksum_match(const svn_checksum_t *checksum1,
255                    const svn_checksum_t *checksum2)
256 {
257   if (checksum1 == NULL || checksum2 == NULL)
258     return TRUE;
259
260   if (checksum1->kind != checksum2->kind)
261     return FALSE;
262
263   switch (checksum1->kind)
264     {
265       case svn_checksum_md5:
266       case svn_checksum_sha1:
267       case svn_checksum_fnv1a_32:
268       case svn_checksum_fnv1a_32x4:
269         return svn__digests_match(checksum1->digest,
270                                   checksum2->digest,
271                                   digest_sizes[checksum1->kind]);
272
273       default:
274         /* We really shouldn't get here, but if we do... */
275         return FALSE;
276     }
277 }
278
279 const char *
280 svn_checksum_to_cstring_display(const svn_checksum_t *checksum,
281                                 apr_pool_t *pool)
282 {
283   switch (checksum->kind)
284     {
285       case svn_checksum_md5:
286       case svn_checksum_sha1:
287       case svn_checksum_fnv1a_32:
288       case svn_checksum_fnv1a_32x4:
289         return svn__digest_to_cstring_display(checksum->digest,
290                                               digest_sizes[checksum->kind],
291                                               pool);
292
293       default:
294         /* We really shouldn't get here, but if we do... */
295         return NULL;
296     }
297 }
298
299 const char *
300 svn_checksum_to_cstring(const svn_checksum_t *checksum,
301                         apr_pool_t *pool)
302 {
303   if (checksum == NULL)
304     return NULL;
305
306   switch (checksum->kind)
307     {
308       case svn_checksum_md5:
309       case svn_checksum_sha1:
310       case svn_checksum_fnv1a_32:
311       case svn_checksum_fnv1a_32x4:
312         return svn__digest_to_cstring(checksum->digest,
313                                       digest_sizes[checksum->kind],
314                                       pool);
315
316       default:
317         /* We really shouldn't get here, but if we do... */
318         return NULL;
319     }
320 }
321
322
323 const char *
324 svn_checksum_serialize(const svn_checksum_t *checksum,
325                        apr_pool_t *result_pool,
326                        apr_pool_t *scratch_pool)
327 {
328   SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5
329                            || checksum->kind <= svn_checksum_fnv1a_32x4);
330   return apr_pstrcat(result_pool,
331                      ckind_str[checksum->kind],
332                      svn_checksum_to_cstring(checksum, scratch_pool),
333                      SVN_VA_NULL);
334 }
335
336
337 svn_error_t *
338 svn_checksum_deserialize(const svn_checksum_t **checksum,
339                          const char *data,
340                          apr_pool_t *result_pool,
341                          apr_pool_t *scratch_pool)
342 {
343   svn_checksum_kind_t kind;
344   svn_checksum_t *parsed_checksum;
345
346   /* All prefixes have the same length. */
347   apr_size_t prefix_len = strlen(ckind_str[0]);
348
349   /* "$md5 $...", "$sha1$..." or ... */
350   if (strlen(data) <= prefix_len)
351     return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL,
352                              _("Invalid prefix in checksum '%s'"),
353                              data);
354
355   for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
356     if (strncmp(ckind_str[kind], data, prefix_len) == 0)
357       {
358         SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind,
359                                        data + prefix_len, result_pool));
360         *checksum = parsed_checksum;
361         return SVN_NO_ERROR;
362       }
363
364   return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
365                            "Unknown checksum kind in '%s'", data);
366 }
367
368
369 svn_error_t *
370 svn_checksum_parse_hex(svn_checksum_t **checksum,
371                        svn_checksum_kind_t kind,
372                        const char *hex,
373                        apr_pool_t *pool)
374 {
375   apr_size_t i, len;
376   char is_nonzero = '\0';
377   char *digest;
378   static const char xdigitval[256] =
379     {
380       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
381       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
382       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
383        0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,   /* 0-9 */
384       -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* A-F */
385       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
386       -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* a-f */
387       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
388       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
389       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
390       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
391       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
392       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
393       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
394       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
395       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
396     };
397
398   if (hex == NULL)
399     {
400       *checksum = NULL;
401       return SVN_NO_ERROR;
402     }
403
404   SVN_ERR(validate_kind(kind));
405
406   *checksum = svn_checksum_create(kind, pool);
407   digest = (char *)(*checksum)->digest;
408   len = DIGESTSIZE(kind);
409
410   for (i = 0; i < len; i++)
411     {
412       char x1 = xdigitval[(unsigned char)hex[i * 2]];
413       char x2 = xdigitval[(unsigned char)hex[i * 2 + 1]];
414       if (x1 == (char)-1 || x2 == (char)-1)
415         return svn_error_create(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, NULL);
416
417       digest[i] = (char)((x1 << 4) | x2);
418       is_nonzero |= (char)((x1 << 4) | x2);
419     }
420
421   if (!is_nonzero)
422     *checksum = NULL;
423
424   return SVN_NO_ERROR;
425 }
426
427 svn_checksum_t *
428 svn_checksum_dup(const svn_checksum_t *checksum,
429                  apr_pool_t *pool)
430 {
431   /* The duplicate of a NULL checksum is a NULL... */
432   if (checksum == NULL)
433     return NULL;
434
435   /* Without this check on valid checksum kind a NULL svn_checksum_t
436    * pointer is returned which could cause a core dump at an
437    * indeterminate time in the future because callers are not
438    * expecting a NULL pointer.  This commit forces an early abort() so
439    * it's easier to track down where the issue arose. */
440   switch (checksum->kind)
441     {
442       case svn_checksum_md5:
443       case svn_checksum_sha1:
444       case svn_checksum_fnv1a_32:
445       case svn_checksum_fnv1a_32x4:
446         return checksum_create(checksum->kind, checksum->digest, pool);
447
448       default:
449         SVN_ERR_MALFUNCTION_NO_RETURN();
450         break;
451     }
452 }
453
454 svn_error_t *
455 svn_checksum(svn_checksum_t **checksum,
456              svn_checksum_kind_t kind,
457              const void *data,
458              apr_size_t len,
459              apr_pool_t *pool)
460 {
461   apr_sha1_ctx_t sha1_ctx;
462
463   SVN_ERR(validate_kind(kind));
464   *checksum = svn_checksum_create(kind, pool);
465
466   switch (kind)
467     {
468       case svn_checksum_md5:
469         apr_md5((unsigned char *)(*checksum)->digest, data, len);
470         break;
471
472       case svn_checksum_sha1:
473         apr_sha1_init(&sha1_ctx);
474         apr_sha1_update(&sha1_ctx, data, (unsigned int)len);
475         apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx);
476         break;
477
478       case svn_checksum_fnv1a_32:
479         *(apr_uint32_t *)(*checksum)->digest
480           = htonl(svn__fnv1a_32(data, len));
481         break;
482
483       case svn_checksum_fnv1a_32x4:
484         *(apr_uint32_t *)(*checksum)->digest
485           = htonl(svn__fnv1a_32x4(data, len));
486         break;
487
488       default:
489         /* We really shouldn't get here, but if we do... */
490         return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
491     }
492
493   return SVN_NO_ERROR;
494 }
495
496
497 svn_checksum_t *
498 svn_checksum_empty_checksum(svn_checksum_kind_t kind,
499                             apr_pool_t *pool)
500 {
501   switch (kind)
502     {
503       case svn_checksum_md5:
504       case svn_checksum_sha1:
505       case svn_checksum_fnv1a_32:
506       case svn_checksum_fnv1a_32x4:
507         return checksum_create(kind, empty_string_digests[kind], pool);
508
509       default:
510         /* We really shouldn't get here, but if we do... */
511         SVN_ERR_MALFUNCTION_NO_RETURN();
512     }
513 }
514
515 struct svn_checksum_ctx_t
516 {
517   void *apr_ctx;
518   svn_checksum_kind_t kind;
519 };
520
521 svn_checksum_ctx_t *
522 svn_checksum_ctx_create(svn_checksum_kind_t kind,
523                         apr_pool_t *pool)
524 {
525   svn_checksum_ctx_t *ctx = apr_palloc(pool, sizeof(*ctx));
526
527   ctx->kind = kind;
528   switch (kind)
529     {
530       case svn_checksum_md5:
531         ctx->apr_ctx = apr_palloc(pool, sizeof(apr_md5_ctx_t));
532         apr_md5_init(ctx->apr_ctx);
533         break;
534
535       case svn_checksum_sha1:
536         ctx->apr_ctx = apr_palloc(pool, sizeof(apr_sha1_ctx_t));
537         apr_sha1_init(ctx->apr_ctx);
538         break;
539
540       case svn_checksum_fnv1a_32:
541         ctx->apr_ctx = svn_fnv1a_32__context_create(pool);
542         break;
543
544       case svn_checksum_fnv1a_32x4:
545         ctx->apr_ctx = svn_fnv1a_32x4__context_create(pool);
546         break;
547
548       default:
549         SVN_ERR_MALFUNCTION_NO_RETURN();
550     }
551
552   return ctx;
553 }
554
555 svn_error_t *
556 svn_checksum_update(svn_checksum_ctx_t *ctx,
557                     const void *data,
558                     apr_size_t len)
559 {
560   switch (ctx->kind)
561     {
562       case svn_checksum_md5:
563         apr_md5_update(ctx->apr_ctx, data, len);
564         break;
565
566       case svn_checksum_sha1:
567         apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len);
568         break;
569
570       case svn_checksum_fnv1a_32:
571         svn_fnv1a_32__update(ctx->apr_ctx, data, len);
572         break;
573
574       case svn_checksum_fnv1a_32x4:
575         svn_fnv1a_32x4__update(ctx->apr_ctx, data, len);
576         break;
577
578       default:
579         /* We really shouldn't get here, but if we do... */
580         return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
581     }
582
583   return SVN_NO_ERROR;
584 }
585
586 svn_error_t *
587 svn_checksum_final(svn_checksum_t **checksum,
588                    const svn_checksum_ctx_t *ctx,
589                    apr_pool_t *pool)
590 {
591   *checksum = svn_checksum_create(ctx->kind, pool);
592
593   switch (ctx->kind)
594     {
595       case svn_checksum_md5:
596         apr_md5_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
597         break;
598
599       case svn_checksum_sha1:
600         apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
601         break;
602
603       case svn_checksum_fnv1a_32:
604         *(apr_uint32_t *)(*checksum)->digest
605           = htonl(svn_fnv1a_32__finalize(ctx->apr_ctx));
606         break;
607
608       case svn_checksum_fnv1a_32x4:
609         *(apr_uint32_t *)(*checksum)->digest
610           = htonl(svn_fnv1a_32x4__finalize(ctx->apr_ctx));
611         break;
612
613       default:
614         /* We really shouldn't get here, but if we do... */
615         return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
616     }
617
618   return SVN_NO_ERROR;
619 }
620
621 apr_size_t
622 svn_checksum_size(const svn_checksum_t *checksum)
623 {
624   return DIGESTSIZE(checksum->kind);
625 }
626
627 svn_error_t *
628 svn_checksum_mismatch_err(const svn_checksum_t *expected,
629                           const svn_checksum_t *actual,
630                           apr_pool_t *scratch_pool,
631                           const char *fmt,
632                           ...)
633 {
634   va_list ap;
635   const char *desc;
636
637   va_start(ap, fmt);
638   desc = apr_pvsprintf(scratch_pool, fmt, ap);
639   va_end(ap);
640
641   return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
642                            _("%s:\n"
643                              "   expected:  %s\n"
644                              "     actual:  %s\n"),
645                 desc,
646                 svn_checksum_to_cstring_display(expected, scratch_pool),
647                 svn_checksum_to_cstring_display(actual, scratch_pool));
648 }
649
650 svn_boolean_t
651 svn_checksum_is_empty_checksum(svn_checksum_t *checksum)
652 {
653   /* By definition, the NULL checksum matches all others, including the
654      empty one. */
655   if (!checksum)
656     return TRUE;
657
658   switch (checksum->kind)
659     {
660       case svn_checksum_md5:
661       case svn_checksum_sha1:
662       case svn_checksum_fnv1a_32:
663       case svn_checksum_fnv1a_32x4:
664         return svn__digests_match(checksum->digest,
665                                   svn__empty_string_digest(checksum->kind),
666                                   digest_sizes[checksum->kind]);
667
668       default:
669         /* We really shouldn't get here, but if we do... */
670         SVN_ERR_MALFUNCTION_NO_RETURN();
671     }
672 }
673
674 /* Checksum calculating stream wrappers.
675  */
676
677 /* Baton used by write_handler and close_handler to calculate the checksum
678  * and return the result to the stream creator.  It accommodates the data
679  * needed by svn_checksum__wrap_write_stream_fnv1a_32x4 as well as
680  * svn_checksum__wrap_write_stream.
681  */
682 typedef struct stream_baton_t
683 {
684   /* Stream we are wrapping. Forward write() and close() operations to it. */
685   svn_stream_t *inner_stream;
686
687   /* Build the checksum data in here. */
688   svn_checksum_ctx_t *context;
689
690   /* Write the final checksum here. May be NULL. */
691   svn_checksum_t **checksum;
692
693   /* Copy the digest of the final checksum. May be NULL. */
694   unsigned char *digest;
695
696   /* Allocate the resulting checksum here. */
697   apr_pool_t *pool;
698 } stream_baton_t;
699
700 /* Implement svn_write_fn_t.
701  * Update checksum and pass data on to inner stream.
702  */
703 static svn_error_t *
704 write_handler(void *baton,
705               const char *data,
706               apr_size_t *len)
707 {
708   stream_baton_t *b = baton;
709
710   SVN_ERR(svn_checksum_update(b->context, data, *len));
711   SVN_ERR(svn_stream_write(b->inner_stream, data, len));
712
713   return SVN_NO_ERROR;
714 }
715
716 /* Implement svn_close_fn_t.
717  * Finalize checksum calculation and write results. Close inner stream.
718  */
719 static svn_error_t *
720 close_handler(void *baton)
721 {
722   stream_baton_t *b = baton;
723   svn_checksum_t *local_checksum;
724
725   /* Ensure we can always write to *B->CHECKSUM. */
726   if (!b->checksum)
727     b->checksum = &local_checksum;
728
729   /* Get the final checksum. */
730   SVN_ERR(svn_checksum_final(b->checksum, b->context, b->pool));
731
732   /* Extract digest, if wanted. */
733   if (b->digest)
734     {
735       apr_size_t digest_size = DIGESTSIZE((*b->checksum)->kind);
736       memcpy(b->digest, (*b->checksum)->digest, digest_size);
737     }
738
739   /* Done here.  Now, close the underlying stream as well. */
740   return svn_error_trace(svn_stream_close(b->inner_stream));
741 }
742
743 /* Common constructor function for svn_checksum__wrap_write_stream and
744  * svn_checksum__wrap_write_stream_fnv1a_32x4, taking the superset of their
745  * respecting parameters.
746  *
747  * In the current usage, either CHECKSUM or DIGEST will be NULL but this
748  * function does not enforce any such restriction.  Also, the caller must
749  * make sure that DIGEST refers to a buffer of sufficient length.
750  */
751 static svn_stream_t *
752 wrap_write_stream(svn_checksum_t **checksum,
753                   unsigned char *digest,
754                   svn_stream_t *inner_stream,
755                   svn_checksum_kind_t kind,
756                   apr_pool_t *pool)
757 {
758   svn_stream_t *outer_stream;
759
760   stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
761   baton->inner_stream = inner_stream;
762   baton->context = svn_checksum_ctx_create(kind, pool);
763   baton->checksum = checksum;
764   baton->digest = digest;
765   baton->pool = pool;
766
767   outer_stream = svn_stream_create(baton, pool);
768   svn_stream_set_write(outer_stream, write_handler);
769   svn_stream_set_close(outer_stream, close_handler);
770
771   return outer_stream;
772 }
773
774 svn_stream_t *
775 svn_checksum__wrap_write_stream(svn_checksum_t **checksum,
776                                 svn_stream_t *inner_stream,
777                                 svn_checksum_kind_t kind,
778                                 apr_pool_t *pool)
779 {
780   return wrap_write_stream(checksum, NULL, inner_stream, kind, pool);
781 }
782
783 /* Implement svn_close_fn_t.
784  * For FNV-1a-like checksums, we want the checksum as 32 bit integer instead
785  * of a big endian 4 byte sequence.  This simply wraps close_handler adding
786  * the digest conversion.
787  */
788 static svn_error_t *
789 close_handler_fnv1a_32x4(void *baton)
790 {
791   stream_baton_t *b = baton;
792   SVN_ERR(close_handler(baton));
793
794   *(apr_uint32_t *)b->digest = ntohl(*(apr_uint32_t *)b->digest);
795   return SVN_NO_ERROR;
796 }
797
798 svn_stream_t *
799 svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest,
800                                            svn_stream_t *inner_stream,
801                                            apr_pool_t *pool)
802 {
803   svn_stream_t *result
804     = wrap_write_stream(NULL, (unsigned char *)digest, inner_stream,
805                         svn_checksum_fnv1a_32x4, pool);
806   svn_stream_set_close(result, close_handler_fnv1a_32x4);
807
808   return result;
809 }