Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
subversion/subversion/libsvn_subr/checksum.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
826 lines (694 sloc)
23.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * checksum.c: checksum routines | |
| * | |
| * ==================================================================== | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| * ==================================================================== | |
| */ | |
| #define APR_WANT_BYTEFUNC | |
| #include <ctype.h> | |
| #include <apr_md5.h> | |
| #include <apr_sha1.h> | |
| #include "svn_checksum.h" | |
| #include "svn_error.h" | |
| #include "svn_ctype.h" | |
| #include "svn_sorts.h" | |
| #include "checksum.h" | |
| #include "fnv1a.h" | |
| #include "private/svn_subr_private.h" | |
| #include "svn_private_config.h" | |
| /* The MD5 digest for the empty string. */ | |
| static const unsigned char md5_empty_string_digest_array[] = { | |
| 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, | |
| 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e | |
| }; | |
| /* The SHA1 digest for the empty string. */ | |
| static const unsigned char sha1_empty_string_digest_array[] = { | |
| 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, | |
| 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 | |
| }; | |
| /* The FNV-1a digest for the empty string. */ | |
| static const unsigned char fnv1a_32_empty_string_digest_array[] = { | |
| 0x81, 0x1c, 0x9d, 0xc5 | |
| }; | |
| /* The FNV-1a digest for the empty string. */ | |
| static const unsigned char fnv1a_32x4_empty_string_digest_array[] = { | |
| 0xcd, 0x6d, 0x9a, 0x85 | |
| }; | |
| /* Digests for an empty string, indexed by checksum type */ | |
| static const unsigned char * empty_string_digests[] = { | |
| md5_empty_string_digest_array, | |
| sha1_empty_string_digest_array, | |
| fnv1a_32_empty_string_digest_array, | |
| fnv1a_32x4_empty_string_digest_array | |
| }; | |
| /* Digest sizes in bytes, indexed by checksum type */ | |
| static const apr_size_t digest_sizes[] = { | |
| APR_MD5_DIGESTSIZE, | |
| APR_SHA1_DIGESTSIZE, | |
| sizeof(apr_uint32_t), | |
| sizeof(apr_uint32_t) | |
| }; | |
| /* Checksum type prefixes used in serialized checksums. */ | |
| static const char *ckind_str[] = { | |
| "$md5 $", | |
| "$sha1$", | |
| "$fnv1$", | |
| "$fnvm$", | |
| /* ### svn_checksum_deserialize() assumes all these have the same strlen() */ | |
| }; | |
| /* Returns the digest size of it's argument. */ | |
| #define DIGESTSIZE(k) \ | |
| (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k]) | |
| /* Largest supported digest size */ | |
| #define MAX_DIGESTSIZE (MAX(APR_MD5_DIGESTSIZE,APR_SHA1_DIGESTSIZE)) | |
| const unsigned char * | |
| svn__empty_string_digest(svn_checksum_kind_t kind) | |
| { | |
| return empty_string_digests[kind]; | |
| } | |
| const char * | |
| svn__digest_to_cstring_display(const unsigned char digest[], | |
| apr_size_t digest_size, | |
| apr_pool_t *pool) | |
| { | |
| static const char *hex = "0123456789abcdef"; | |
| char *str = apr_palloc(pool, (digest_size * 2) + 1); | |
| apr_size_t i; | |
| for (i = 0; i < digest_size; i++) | |
| { | |
| str[i*2] = hex[digest[i] >> 4]; | |
| str[i*2+1] = hex[digest[i] & 0x0f]; | |
| } | |
| str[i*2] = '\0'; | |
| return str; | |
| } | |
| const char * | |
| svn__digest_to_cstring(const unsigned char digest[], | |
| apr_size_t digest_size, | |
| apr_pool_t *pool) | |
| { | |
| static const unsigned char zeros_digest[MAX_DIGESTSIZE] = { 0 }; | |
| if (memcmp(digest, zeros_digest, digest_size) != 0) | |
| return svn__digest_to_cstring_display(digest, digest_size, pool); | |
| else | |
| return NULL; | |
| } | |
| svn_boolean_t | |
| svn__digests_match(const unsigned char d1[], | |
| const unsigned char d2[], | |
| apr_size_t digest_size) | |
| { | |
| static const unsigned char zeros[MAX_DIGESTSIZE] = { 0 }; | |
| return ((memcmp(d1, d2, digest_size) == 0) | |
| || (memcmp(d2, zeros, digest_size) == 0) | |
| || (memcmp(d1, zeros, digest_size) == 0)); | |
| } | |
| /* Check to see if KIND is something we recognize. If not, return | |
| * SVN_ERR_BAD_CHECKSUM_KIND */ | |
| static svn_error_t * | |
| validate_kind(svn_checksum_kind_t kind) | |
| { | |
| if (kind >= svn_checksum_md5 && kind <= svn_checksum_fnv1a_32x4) | |
| return SVN_NO_ERROR; | |
| else | |
| return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); | |
| } | |
| /* Create a svn_checksum_t with everything but the contents of the | |
| digest populated. */ | |
| static svn_checksum_t * | |
| checksum_create_without_digest(svn_checksum_kind_t kind, | |
| apr_size_t digest_size, | |
| apr_pool_t *pool) | |
| { | |
| /* Use apr_palloc() instead of apr_pcalloc() so that the digest | |
| * contents are only set once by the caller. */ | |
| svn_checksum_t *checksum = apr_palloc(pool, sizeof(*checksum) + digest_size); | |
| checksum->digest = (unsigned char *)checksum + sizeof(*checksum); | |
| checksum->kind = kind; | |
| return checksum; | |
| } | |
| /* Return a checksum object, allocated in POOL. The checksum will be of | |
| * type KIND and contain the given DIGEST. | |
| */ | |
| static svn_checksum_t * | |
| checksum_create(svn_checksum_kind_t kind, | |
| const unsigned char *digest, | |
| apr_pool_t *pool) | |
| { | |
| apr_size_t digest_size = DIGESTSIZE(kind); | |
| svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size, | |
| pool); | |
| memcpy((unsigned char *)checksum->digest, digest, digest_size); | |
| return checksum; | |
| } | |
| svn_checksum_t * | |
| svn_checksum_create(svn_checksum_kind_t kind, | |
| apr_pool_t *pool) | |
| { | |
| svn_checksum_t *checksum; | |
| apr_size_t digest_size; | |
| switch (kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| digest_size = digest_sizes[kind]; | |
| break; | |
| default: | |
| return NULL; | |
| } | |
| checksum = checksum_create_without_digest(kind, digest_size, pool); | |
| memset((unsigned char *) checksum->digest, 0, digest_size); | |
| return checksum; | |
| } | |
| svn_checksum_t * | |
| svn_checksum__from_digest_md5(const unsigned char *digest, | |
| apr_pool_t *result_pool) | |
| { | |
| return checksum_create(svn_checksum_md5, digest, result_pool); | |
| } | |
| svn_checksum_t * | |
| svn_checksum__from_digest_sha1(const unsigned char *digest, | |
| apr_pool_t *result_pool) | |
| { | |
| return checksum_create(svn_checksum_sha1, digest, result_pool); | |
| } | |
| svn_checksum_t * | |
| svn_checksum__from_digest_fnv1a_32(const unsigned char *digest, | |
| apr_pool_t *result_pool) | |
| { | |
| return checksum_create(svn_checksum_fnv1a_32, digest, result_pool); | |
| } | |
| svn_checksum_t * | |
| svn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest, | |
| apr_pool_t *result_pool) | |
| { | |
| return checksum_create(svn_checksum_fnv1a_32x4, digest, result_pool); | |
| } | |
| svn_error_t * | |
| svn_checksum_clear(svn_checksum_t *checksum) | |
| { | |
| SVN_ERR(validate_kind(checksum->kind)); | |
| memset((unsigned char *) checksum->digest, 0, DIGESTSIZE(checksum->kind)); | |
| return SVN_NO_ERROR; | |
| } | |
| svn_boolean_t | |
| svn_checksum_match(const svn_checksum_t *checksum1, | |
| const svn_checksum_t *checksum2) | |
| { | |
| if (checksum1 == NULL || checksum2 == NULL) | |
| return TRUE; | |
| if (checksum1->kind != checksum2->kind) | |
| return FALSE; | |
| switch (checksum1->kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| return svn__digests_match(checksum1->digest, | |
| checksum2->digest, | |
| digest_sizes[checksum1->kind]); | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| return FALSE; | |
| } | |
| } | |
| const char * | |
| svn_checksum_to_cstring_display(const svn_checksum_t *checksum, | |
| apr_pool_t *pool) | |
| { | |
| switch (checksum->kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| return svn__digest_to_cstring_display(checksum->digest, | |
| digest_sizes[checksum->kind], | |
| pool); | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| return NULL; | |
| } | |
| } | |
| const char * | |
| svn_checksum_to_cstring(const svn_checksum_t *checksum, | |
| apr_pool_t *pool) | |
| { | |
| if (checksum == NULL) | |
| return NULL; | |
| switch (checksum->kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| return svn__digest_to_cstring(checksum->digest, | |
| digest_sizes[checksum->kind], | |
| pool); | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| return NULL; | |
| } | |
| } | |
| const char * | |
| svn_checksum_serialize(const svn_checksum_t *checksum, | |
| apr_pool_t *result_pool, | |
| apr_pool_t *scratch_pool) | |
| { | |
| SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5 | |
| || checksum->kind <= svn_checksum_fnv1a_32x4); | |
| return apr_pstrcat(result_pool, | |
| ckind_str[checksum->kind], | |
| svn_checksum_to_cstring(checksum, scratch_pool), | |
| SVN_VA_NULL); | |
| } | |
| svn_error_t * | |
| svn_checksum_deserialize(const svn_checksum_t **checksum, | |
| const char *data, | |
| apr_pool_t *result_pool, | |
| apr_pool_t *scratch_pool) | |
| { | |
| svn_checksum_kind_t kind; | |
| svn_checksum_t *parsed_checksum; | |
| /* All prefixes have the same length. */ | |
| apr_size_t prefix_len = strlen(ckind_str[0]); | |
| /* "$md5 $...", "$sha1$..." or ... */ | |
| if (strlen(data) <= prefix_len) | |
| return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, | |
| _("Invalid prefix in checksum '%s'"), | |
| data); | |
| for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind) | |
| if (strncmp(ckind_str[kind], data, prefix_len) == 0) | |
| { | |
| SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind, | |
| data + prefix_len, result_pool)); | |
| *checksum = parsed_checksum; | |
| return SVN_NO_ERROR; | |
| } | |
| return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL, | |
| "Unknown checksum kind in '%s'", data); | |
| } | |
| svn_error_t * | |
| svn_checksum_parse_hex(svn_checksum_t **checksum, | |
| svn_checksum_kind_t kind, | |
| const char *hex, | |
| apr_pool_t *pool) | |
| { | |
| apr_size_t i, len; | |
| unsigned char is_nonzero = 0; | |
| unsigned char *digest; | |
| static const unsigned char xdigitval[256] = | |
| { | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, /* 0-7 */ | |
| 0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, /* 8-9 */ | |
| 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF, /* A-F */ | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF, /* a-f */ | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
| }; | |
| if (hex == NULL) | |
| { | |
| *checksum = NULL; | |
| return SVN_NO_ERROR; | |
| } | |
| SVN_ERR(validate_kind(kind)); | |
| *checksum = svn_checksum_create(kind, pool); | |
| digest = (unsigned char *)(*checksum)->digest; | |
| len = DIGESTSIZE(kind); | |
| for (i = 0; i < len; i++) | |
| { | |
| unsigned char x1 = xdigitval[(unsigned char)hex[i * 2]]; | |
| unsigned char x2 = xdigitval[(unsigned char)hex[i * 2 + 1]]; | |
| if (x1 == 0xFF || x2 == 0xFF) | |
| return svn_error_create(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, NULL); | |
| digest[i] = (x1 << 4) | x2; | |
| is_nonzero |= digest[i]; | |
| } | |
| if (!is_nonzero) | |
| *checksum = NULL; | |
| return SVN_NO_ERROR; | |
| } | |
| svn_checksum_t * | |
| svn_checksum_dup(const svn_checksum_t *checksum, | |
| apr_pool_t *pool) | |
| { | |
| /* The duplicate of a NULL checksum is a NULL... */ | |
| if (checksum == NULL) | |
| return NULL; | |
| /* Without this check on valid checksum kind a NULL svn_checksum_t | |
| * pointer is returned which could cause a core dump at an | |
| * indeterminate time in the future because callers are not | |
| * expecting a NULL pointer. This commit forces an early abort() so | |
| * it's easier to track down where the issue arose. */ | |
| switch (checksum->kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| return checksum_create(checksum->kind, checksum->digest, pool); | |
| default: | |
| SVN_ERR_MALFUNCTION_NO_RETURN(); | |
| break; | |
| } | |
| } | |
| svn_error_t * | |
| svn_checksum(svn_checksum_t **checksum, | |
| svn_checksum_kind_t kind, | |
| const void *data, | |
| apr_size_t len, | |
| apr_pool_t *pool) | |
| { | |
| apr_sha1_ctx_t sha1_ctx; | |
| SVN_ERR(validate_kind(kind)); | |
| *checksum = svn_checksum_create(kind, pool); | |
| switch (kind) | |
| { | |
| case svn_checksum_md5: | |
| apr_md5((unsigned char *)(*checksum)->digest, data, len); | |
| break; | |
| case svn_checksum_sha1: | |
| apr_sha1_init(&sha1_ctx); | |
| apr_sha1_update(&sha1_ctx, data, (unsigned int)len); | |
| apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx); | |
| break; | |
| case svn_checksum_fnv1a_32: | |
| *(apr_uint32_t *)(*checksum)->digest | |
| = htonl(svn__fnv1a_32(data, len)); | |
| break; | |
| case svn_checksum_fnv1a_32x4: | |
| *(apr_uint32_t *)(*checksum)->digest | |
| = htonl(svn__fnv1a_32x4(data, len)); | |
| break; | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); | |
| } | |
| return SVN_NO_ERROR; | |
| } | |
| svn_checksum_t * | |
| svn_checksum_empty_checksum(svn_checksum_kind_t kind, | |
| apr_pool_t *pool) | |
| { | |
| switch (kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| return checksum_create(kind, empty_string_digests[kind], pool); | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| SVN_ERR_MALFUNCTION_NO_RETURN(); | |
| } | |
| } | |
| struct svn_checksum_ctx_t | |
| { | |
| void *apr_ctx; | |
| svn_checksum_kind_t kind; | |
| }; | |
| svn_checksum_ctx_t * | |
| svn_checksum_ctx_create(svn_checksum_kind_t kind, | |
| apr_pool_t *pool) | |
| { | |
| svn_checksum_ctx_t *ctx = apr_palloc(pool, sizeof(*ctx)); | |
| ctx->kind = kind; | |
| switch (kind) | |
| { | |
| case svn_checksum_md5: | |
| ctx->apr_ctx = apr_palloc(pool, sizeof(apr_md5_ctx_t)); | |
| apr_md5_init(ctx->apr_ctx); | |
| break; | |
| case svn_checksum_sha1: | |
| ctx->apr_ctx = apr_palloc(pool, sizeof(apr_sha1_ctx_t)); | |
| apr_sha1_init(ctx->apr_ctx); | |
| break; | |
| case svn_checksum_fnv1a_32: | |
| ctx->apr_ctx = svn_fnv1a_32__context_create(pool); | |
| break; | |
| case svn_checksum_fnv1a_32x4: | |
| ctx->apr_ctx = svn_fnv1a_32x4__context_create(pool); | |
| break; | |
| default: | |
| SVN_ERR_MALFUNCTION_NO_RETURN(); | |
| } | |
| return ctx; | |
| } | |
| svn_error_t * | |
| svn_checksum_update(svn_checksum_ctx_t *ctx, | |
| const void *data, | |
| apr_size_t len) | |
| { | |
| switch (ctx->kind) | |
| { | |
| case svn_checksum_md5: | |
| apr_md5_update(ctx->apr_ctx, data, len); | |
| break; | |
| case svn_checksum_sha1: | |
| apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len); | |
| break; | |
| case svn_checksum_fnv1a_32: | |
| svn_fnv1a_32__update(ctx->apr_ctx, data, len); | |
| break; | |
| case svn_checksum_fnv1a_32x4: | |
| svn_fnv1a_32x4__update(ctx->apr_ctx, data, len); | |
| break; | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); | |
| } | |
| return SVN_NO_ERROR; | |
| } | |
| svn_error_t * | |
| svn_checksum_final(svn_checksum_t **checksum, | |
| const svn_checksum_ctx_t *ctx, | |
| apr_pool_t *pool) | |
| { | |
| *checksum = svn_checksum_create(ctx->kind, pool); | |
| switch (ctx->kind) | |
| { | |
| case svn_checksum_md5: | |
| apr_md5_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx); | |
| break; | |
| case svn_checksum_sha1: | |
| apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx); | |
| break; | |
| case svn_checksum_fnv1a_32: | |
| *(apr_uint32_t *)(*checksum)->digest | |
| = htonl(svn_fnv1a_32__finalize(ctx->apr_ctx)); | |
| break; | |
| case svn_checksum_fnv1a_32x4: | |
| *(apr_uint32_t *)(*checksum)->digest | |
| = htonl(svn_fnv1a_32x4__finalize(ctx->apr_ctx)); | |
| break; | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL); | |
| } | |
| return SVN_NO_ERROR; | |
| } | |
| apr_size_t | |
| svn_checksum_size(const svn_checksum_t *checksum) | |
| { | |
| return DIGESTSIZE(checksum->kind); | |
| } | |
| svn_error_t * | |
| svn_checksum_mismatch_err(const svn_checksum_t *expected, | |
| const svn_checksum_t *actual, | |
| apr_pool_t *scratch_pool, | |
| const char *fmt, | |
| ...) | |
| { | |
| va_list ap; | |
| const char *desc; | |
| va_start(ap, fmt); | |
| desc = apr_pvsprintf(scratch_pool, fmt, ap); | |
| va_end(ap); | |
| return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL, | |
| _("%s:\n" | |
| " expected: %s\n" | |
| " actual: %s\n"), | |
| desc, | |
| svn_checksum_to_cstring_display(expected, scratch_pool), | |
| svn_checksum_to_cstring_display(actual, scratch_pool)); | |
| } | |
| svn_boolean_t | |
| svn_checksum_is_empty_checksum(svn_checksum_t *checksum) | |
| { | |
| /* By definition, the NULL checksum matches all others, including the | |
| empty one. */ | |
| if (!checksum) | |
| return TRUE; | |
| switch (checksum->kind) | |
| { | |
| case svn_checksum_md5: | |
| case svn_checksum_sha1: | |
| case svn_checksum_fnv1a_32: | |
| case svn_checksum_fnv1a_32x4: | |
| return svn__digests_match(checksum->digest, | |
| svn__empty_string_digest(checksum->kind), | |
| digest_sizes[checksum->kind]); | |
| default: | |
| /* We really shouldn't get here, but if we do... */ | |
| SVN_ERR_MALFUNCTION_NO_RETURN(); | |
| } | |
| } | |
| /* Checksum calculating stream wrappers. | |
| */ | |
| /* Baton used by write_handler and close_handler to calculate the checksum | |
| * and return the result to the stream creator. It accommodates the data | |
| * needed by svn_checksum__wrap_write_stream_fnv1a_32x4 as well as | |
| * svn_checksum__wrap_write_stream. | |
| */ | |
| typedef struct stream_baton_t | |
| { | |
| /* Stream we are wrapping. Forward write() and close() operations to it. */ | |
| svn_stream_t *inner_stream; | |
| /* Build the checksum data in here. */ | |
| svn_checksum_ctx_t *context; | |
| /* Write the final checksum here. May be NULL. */ | |
| svn_checksum_t **checksum; | |
| /* Copy the digest of the final checksum. May be NULL. */ | |
| unsigned char *digest; | |
| /* Allocate the resulting checksum here. */ | |
| apr_pool_t *pool; | |
| } stream_baton_t; | |
| /* Implement svn_write_fn_t. | |
| * Update checksum and pass data on to inner stream. | |
| */ | |
| static svn_error_t * | |
| write_handler(void *baton, | |
| const char *data, | |
| apr_size_t *len) | |
| { | |
| stream_baton_t *b = baton; | |
| SVN_ERR(svn_checksum_update(b->context, data, *len)); | |
| SVN_ERR(svn_stream_write(b->inner_stream, data, len)); | |
| return SVN_NO_ERROR; | |
| } | |
| /* Implement svn_close_fn_t. | |
| * Finalize checksum calculation and write results. Close inner stream. | |
| */ | |
| static svn_error_t * | |
| close_handler(void *baton) | |
| { | |
| stream_baton_t *b = baton; | |
| svn_checksum_t *local_checksum; | |
| /* Ensure we can always write to *B->CHECKSUM. */ | |
| if (!b->checksum) | |
| b->checksum = &local_checksum; | |
| /* Get the final checksum. */ | |
| SVN_ERR(svn_checksum_final(b->checksum, b->context, b->pool)); | |
| /* Extract digest, if wanted. */ | |
| if (b->digest) | |
| { | |
| apr_size_t digest_size = DIGESTSIZE((*b->checksum)->kind); | |
| memcpy(b->digest, (*b->checksum)->digest, digest_size); | |
| } | |
| /* Done here. Now, close the underlying stream as well. */ | |
| return svn_error_trace(svn_stream_close(b->inner_stream)); | |
| } | |
| /* Common constructor function for svn_checksum__wrap_write_stream and | |
| * svn_checksum__wrap_write_stream_fnv1a_32x4, taking the superset of their | |
| * respecting parameters. | |
| * | |
| * In the current usage, either CHECKSUM or DIGEST will be NULL but this | |
| * function does not enforce any such restriction. Also, the caller must | |
| * make sure that DIGEST refers to a buffer of sufficient length. | |
| */ | |
| static svn_stream_t * | |
| wrap_write_stream(svn_checksum_t **checksum, | |
| unsigned char *digest, | |
| svn_stream_t *inner_stream, | |
| svn_checksum_kind_t kind, | |
| apr_pool_t *pool) | |
| { | |
| svn_stream_t *outer_stream; | |
| stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton)); | |
| baton->inner_stream = inner_stream; | |
| baton->context = svn_checksum_ctx_create(kind, pool); | |
| baton->checksum = checksum; | |
| baton->digest = digest; | |
| baton->pool = pool; | |
| outer_stream = svn_stream_create(baton, pool); | |
| svn_stream_set_write(outer_stream, write_handler); | |
| svn_stream_set_close(outer_stream, close_handler); | |
| return outer_stream; | |
| } | |
| svn_stream_t * | |
| svn_checksum__wrap_write_stream(svn_checksum_t **checksum, | |
| svn_stream_t *inner_stream, | |
| svn_checksum_kind_t kind, | |
| apr_pool_t *pool) | |
| { | |
| return wrap_write_stream(checksum, NULL, inner_stream, kind, pool); | |
| } | |
| /* Implement svn_close_fn_t. | |
| * For FNV-1a-like checksums, we want the checksum as 32 bit integer instead | |
| * of a big endian 4 byte sequence. This simply wraps close_handler adding | |
| * the digest conversion. | |
| */ | |
| static svn_error_t * | |
| close_handler_fnv1a_32x4(void *baton) | |
| { | |
| stream_baton_t *b = baton; | |
| SVN_ERR(close_handler(baton)); | |
| *(apr_uint32_t *)b->digest = ntohl(*(apr_uint32_t *)b->digest); | |
| return SVN_NO_ERROR; | |
| } | |
| svn_stream_t * | |
| svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest, | |
| svn_stream_t *inner_stream, | |
| apr_pool_t *pool) | |
| { | |
| svn_stream_t *result | |
| = wrap_write_stream(NULL, (unsigned char *)digest, inner_stream, | |
| svn_checksum_fnv1a_32x4, pool); | |
| svn_stream_set_close(result, close_handler_fnv1a_32x4); | |
| return result; | |
| } |