blob: 0d6a0c6ee334fc9c9c25696666df71df48e21597 [file] [log] [blame]
/*
* 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_ctx_reset(svn_checksum_ctx_t *ctx)
{
switch (ctx->kind)
{
case svn_checksum_md5:
memset(ctx->apr_ctx, 0, sizeof(apr_md5_ctx_t));
apr_md5_init(ctx->apr_ctx);
break;
case svn_checksum_sha1:
memset(ctx->apr_ctx, 0, sizeof(apr_sha1_ctx_t));
apr_sha1_init(ctx->apr_ctx);
break;
case svn_checksum_fnv1a_32:
svn_fnv1a_32__context_reset(ctx->apr_ctx);
break;
case svn_checksum_fnv1a_32x4:
svn_fnv1a_32x4__context_reset(ctx->apr_ctx);
break;
default:
SVN_ERR_MALFUNCTION();
}
return SVN_NO_ERROR;
}
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;
}