checksum.c revision 299742
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 44 45/* The MD5 digest for the empty string. */ 46static 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. */ 52static 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. */ 58static 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. */ 63static 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 */ 68static 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 */ 76static 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. */ 84static 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 98const unsigned char * 99svn__empty_string_digest(svn_checksum_kind_t kind) 100{ 101 return empty_string_digests[kind]; 102} 103 104const char * 105svn__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 124const char * 125svn__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 138svn_boolean_t 139svn__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 */ 152static svn_error_t * 153validate_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. */ 163static svn_checksum_t * 164checksum_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 */ 179static svn_checksum_t * 180checksum_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 191svn_checksum_t * 192svn_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 216svn_checksum_t * 217svn_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 223svn_checksum_t * 224svn_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 230svn_checksum_t * 231svn_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 237svn_checksum_t * 238svn_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 244svn_error_t * 245svn_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 253svn_boolean_t 254svn_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 279const char * 280svn_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 299const char * 300svn_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 323const char * 324svn_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 337svn_error_t * 338svn_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 369svn_error_t * 370svn_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 427svn_checksum_t * 428svn_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 454svn_error_t * 455svn_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 497svn_checksum_t * 498svn_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 515struct svn_checksum_ctx_t 516{ 517 void *apr_ctx; 518 svn_checksum_kind_t kind; 519}; 520 521svn_checksum_ctx_t * 522svn_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 555svn_error_t * 556svn_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 586svn_error_t * 587svn_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 621apr_size_t 622svn_checksum_size(const svn_checksum_t *checksum) 623{ 624 return DIGESTSIZE(checksum->kind); 625} 626 627svn_error_t * 628svn_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 650svn_boolean_t 651svn_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 */ 682typedef 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 */ 703static svn_error_t * 704write_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 */ 719static svn_error_t * 720close_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 */ 751static svn_stream_t * 752wrap_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 774svn_stream_t * 775svn_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 */ 788static svn_error_t * 789close_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 798svn_stream_t * 799svn_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} 810