mirror of
https://github.com/Stichting-MINIX-Research-Foundation/pkgsrc-ng.git
synced 2025-09-13 22:36:06 -04:00
375 lines
9.5 KiB
C
375 lines
9.5 KiB
C
/*-
|
|
* Copyright (c) 2013 Alistair Crooks <agc@NetBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/param.h>
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "multigest.h"
|
|
#include "merkletree.h"
|
|
|
|
#define MERKLETREE_MAX_ROWS 16
|
|
|
|
/* a row of digests in a merkle tree */
|
|
typedef struct merkletree_row_t {
|
|
uint64_t outc; /* # of output bytes */
|
|
uint64_t blocks; /* # of blocks in this row */
|
|
char *out; /* sums for this row */
|
|
} merkletree_row_t;
|
|
|
|
/* a tree of rows */
|
|
typedef struct Rows {
|
|
uint64_t height; /* # of row in tree */
|
|
merkletree_row_t row[MERKLETREE_MAX_ROWS]; /* rows of digests in tree */
|
|
} Rows;
|
|
|
|
|
|
/* round up to correct number of blocks */
|
|
static inline uint64_t
|
|
numblocks(uint64_t size, uint64_t blocksize)
|
|
{
|
|
return (size + (blocksize - 1)) / blocksize;
|
|
}
|
|
|
|
/* dynamically allocate the tree */
|
|
static int
|
|
allocate_tree(merkletree_t *tree, uint64_t size, size_t h)
|
|
{
|
|
Rows *rows = (Rows *)tree->rows;
|
|
|
|
if (h == 0) {
|
|
rows->row[h].outc = size;
|
|
}
|
|
rows->row[h].blocks = numblocks(size, tree->blocksize);
|
|
rows->row[h].out = calloc(1, rows->row[h].blocks * tree->rawoutsize);
|
|
if (size > 0 && rows->row[h].blocks > 1) {
|
|
return allocate_tree(tree, size / (tree->blocksize * tree->rawoutsize), ++rows->height);
|
|
}
|
|
return (int)++rows->height;
|
|
}
|
|
|
|
/* calculate a row in a tree */
|
|
static int
|
|
calc_row(merkletree_t *tree, FILE *fp, char *v, size_t size, size_t h)
|
|
{
|
|
uint8_t tmp[8192];
|
|
size_t xfer;
|
|
size_t cc;
|
|
size_t b;
|
|
Rows *rows = (Rows *)tree->rows;
|
|
|
|
rows->row[h].outc = 0;
|
|
for (b = cc = 0 ; cc < size; b++, cc += xfer) {
|
|
if (fp) {
|
|
xfer = fread(v, 1, tree->blocksize, fp);
|
|
multigest_data(tree->algname, (const char *)v, xfer, tmp, NULL, NULL);
|
|
multigest_format_raw(tmp, tree->rawoutsize / 2,
|
|
&rows->row[h].out[rows->row[h].outc], tree->rawoutsize);
|
|
} else {
|
|
xfer = MIN(size - cc, tree->blocksize);
|
|
multigest_data(tree->algname, (const char *)&v[cc], xfer,
|
|
tmp, NULL, NULL);
|
|
multigest_format_raw(tmp, tree->rawoutsize / 2,
|
|
&rows->row[h].out[rows->row[h].outc], tree->rawoutsize);
|
|
}
|
|
rows->row[h].outc += tree->rawoutsize;
|
|
}
|
|
if (h < rows->height - 1) {
|
|
return calc_row(tree, NULL, rows->row[h].out, rows->row[h].outc, h + 1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* skip over initial stuff */
|
|
static inline char *
|
|
skip_to_sum(char *s)
|
|
{
|
|
char *ret;
|
|
char *eq;
|
|
|
|
if ((eq = strchr(s, '=')) == NULL) {
|
|
ret = s;
|
|
} else {
|
|
for (ret = eq + 1; *ret == ' ' ; ret++) {
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* get the col'th column */
|
|
static inline char *
|
|
get_column(char *s, size_t col)
|
|
{
|
|
size_t i;
|
|
char *space;
|
|
char *p;
|
|
|
|
for (space = s, i = 0 ; i < col ; i++, space = p + 1) {
|
|
if ((p = strchr(space + 1, ' ')) == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return space;
|
|
}
|
|
|
|
/* set up the tree */
|
|
static inline int
|
|
setup_tree(merkletree_t *tree, size_t size, const char *alg, size_t blocksize)
|
|
{
|
|
if (tree == NULL || alg == NULL || blocksize == 0) {
|
|
fprintf(stderr, "bad arg\n");
|
|
return 0;
|
|
}
|
|
memset(tree, 0x0, sizeof(*tree));
|
|
if ((tree->rawoutsize = multigest_algs_rawsize(alg) * 2) == 0) {
|
|
fprintf(stderr, "unrecognised algorithm '%s'\n", alg);
|
|
return 0;
|
|
}
|
|
snprintf(tree->algname, sizeof(tree->algname), "%s", alg);
|
|
tree->size = size;
|
|
tree->blocksize = blocksize;
|
|
tree->rows = calloc(1, sizeof(Rows));
|
|
allocate_tree(tree, tree->size, 0);
|
|
return 1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
/* calculate a merkle tree on data */
|
|
int
|
|
merkletree_data(merkletree_t *tree, const void *v, size_t size, const char *alg, size_t blocksize)
|
|
{
|
|
if (!setup_tree(tree, size, alg, blocksize)) {
|
|
return 0;
|
|
}
|
|
return calc_row(tree, NULL, __UNCONST(v), tree->size, 0);
|
|
}
|
|
|
|
/* calculate a merkle tree on a file */
|
|
int
|
|
merkletree_file(merkletree_t *tree, const char *f, const char *alg, size_t blocksize)
|
|
{
|
|
struct stat st;
|
|
size_t size;
|
|
char *mapped;
|
|
char *alloc;
|
|
FILE *fp;
|
|
int ret;
|
|
|
|
if (tree == NULL || f == NULL || alg == NULL || blocksize == 0) {
|
|
return 0;
|
|
}
|
|
if ((fp = fopen(f, "r")) == NULL) {
|
|
fprintf(stderr, "can't open file '%s'\n", f);
|
|
return 0;
|
|
}
|
|
ret = 0;
|
|
fstat(fileno(fp), &st);
|
|
size = st.st_size;
|
|
alloc = NULL;
|
|
if ((mapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fileno(fp), 0)) == MAP_FAILED) {
|
|
if ((alloc = calloc(1, blocksize)) == NULL) {
|
|
goto done;
|
|
}
|
|
if (!setup_tree(tree, size, alg, blocksize)) {
|
|
goto done;
|
|
}
|
|
ret = calc_row(tree, fp, alloc, tree->size, 0);
|
|
} else {
|
|
ret = merkletree_data(tree, mapped, size, alg, blocksize);
|
|
}
|
|
done:
|
|
if (mapped != MAP_FAILED) {
|
|
munmap(mapped, tree->size);
|
|
}
|
|
if (alloc) {
|
|
free(alloc);
|
|
}
|
|
fclose(fp);
|
|
return ret;
|
|
}
|
|
|
|
/* format a merkle tree */
|
|
int
|
|
merkletree_snprintf(merkletree_t *tree, const char *f, char *buf, size_t size)
|
|
{
|
|
size_t cc;
|
|
size_t h;
|
|
Rows *rows;
|
|
|
|
if (tree != NULL && buf != NULL && size > 0) {
|
|
rows = (Rows *)tree->rows;
|
|
cc = 0;
|
|
if (f == NULL) {
|
|
f = "[stdin]";
|
|
}
|
|
cc = snprintf(buf, size,
|
|
"merkle %s %" PRIu64 " %" PRIu64 " %" PRIu64 " (%s) = ",
|
|
tree->algname, rows->height, tree->size, tree->blocksize, f);
|
|
for (h = 0 ; h < rows->height ; h++) {
|
|
cc += snprintf(&buf[cc], size - cc, "%s", rows->row[h].out);
|
|
}
|
|
return (int)cc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* format a merkle tree */
|
|
int
|
|
merkletree_asprintf(merkletree_t *tree, const char *f, char **buf)
|
|
{
|
|
size_t size;
|
|
size_t cc;
|
|
size_t h;
|
|
Rows *rows = (Rows *)tree->rows;
|
|
|
|
if (tree != NULL) {
|
|
if (f == NULL) {
|
|
f = "[stdin]";
|
|
}
|
|
for (size = 0, h = 0 ; h < rows->height ; h++) {
|
|
size += strlen((char *)rows->row[h].out);
|
|
}
|
|
size += strlen(f) + 100;
|
|
if ((*buf = calloc(1, size)) == NULL) {
|
|
return 0;
|
|
}
|
|
cc = snprintf(*buf, size,
|
|
"merkle %s %" PRIu64 " %" PRIu64 " %" PRIu64 " (%s) = ",
|
|
tree->algname, rows->height, tree->size, tree->blocksize, f);
|
|
for (h = 0 ; h < rows->height ; h++) {
|
|
cc += snprintf(&(*buf)[cc], size - cc, "%s", rows->row[h].out);
|
|
}
|
|
return (int)cc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* free resources */
|
|
void
|
|
merkletree_free(merkletree_t *tree)
|
|
{
|
|
uint64_t i;
|
|
Rows *rows;
|
|
|
|
if (tree != NULL) {
|
|
rows = (Rows *)tree->rows;
|
|
for (i = 0 ; i < rows->height ; i++) {
|
|
free(rows->row[i].out);
|
|
}
|
|
free(tree->rows);
|
|
}
|
|
}
|
|
|
|
/* higher level */
|
|
|
|
/* retrieve sum information from a sum file */
|
|
int
|
|
merkletree_sum_info(merkletree_t *tree, const char *f, char **buf)
|
|
{
|
|
struct stat st;
|
|
FILE *fp;
|
|
char *alg;
|
|
char *p;
|
|
int ret;
|
|
|
|
if (tree == NULL || f == NULL || buf == NULL || *buf == NULL) {
|
|
return 0;
|
|
}
|
|
memset(tree, 0x0, sizeof(*tree));
|
|
if ((fp = fopen(f, "r")) == NULL) {
|
|
fprintf(stderr, "can't open '%s'\n", f);
|
|
return 0;
|
|
}
|
|
fstat(fileno(fp), &st);
|
|
*buf = calloc(1, (size_t)(st.st_size + 1));
|
|
ret = (int)fread(*buf, 1, (size_t)(st.st_size - 1), fp);
|
|
fclose(fp);
|
|
(*buf)[ret] = 0x0;
|
|
if ((alg = get_column(*buf, 1)) == NULL) {
|
|
return 0;
|
|
}
|
|
if ((p = get_column(*buf, 2)) == NULL) {
|
|
return 0;
|
|
}
|
|
snprintf(tree->algname, sizeof(tree->algname), "%.*s", (int)(p - alg) - 1, alg);
|
|
if ((tree->rawoutsize = multigest_algs_rawsize(tree->algname) * 2) == 0) {
|
|
fprintf(stderr, "bad alg name '%s'\n", tree->algname);
|
|
return 0;
|
|
}
|
|
if ((p = get_column(*buf, 3)) == NULL) {
|
|
return 0;
|
|
}
|
|
tree->size = (uint64_t)strtoull(p, NULL, 0);
|
|
if ((p = get_column(*buf, 4)) == NULL) {
|
|
return 0;
|
|
}
|
|
tree->blocksize = (uint64_t)strtoull(p, NULL, 0);
|
|
return ret;
|
|
}
|
|
|
|
/* verify a merkletree sum output against a file in the file system */
|
|
int
|
|
merkletree_verify(const char *sumfile, const char *f)
|
|
{
|
|
merkletree_t store;
|
|
merkletree_t calc;
|
|
size_t off;
|
|
size_t len;
|
|
size_t i;
|
|
char *calcsum;
|
|
char *stored;
|
|
char *sum[2];
|
|
|
|
if (sumfile == NULL || f == NULL) {
|
|
return 1;
|
|
}
|
|
memset(&calc, 0x0, sizeof(calc));
|
|
memset(&store, 0x0, sizeof(store));
|
|
if (!merkletree_sum_info(&store, sumfile, &stored)) {
|
|
return 0;
|
|
}
|
|
merkletree_file(&calc, f, store.algname, store.blocksize);
|
|
merkletree_asprintf(&calc, f, &calcsum);
|
|
sum[0] = skip_to_sum(calcsum);
|
|
sum[1] = skip_to_sum(stored);
|
|
if ((len = strlen(sum[0])) == strlen(sum[1]) && memcmp(sum[0], sum[1], len) == 0) {
|
|
return 1;
|
|
}
|
|
for (off = 0, i = 0 ; off < store.size ; i += store.rawoutsize, off += store.blocksize) {
|
|
if (memcmp(&sum[0][i], &sum[1][i], store.rawoutsize) != 0) {
|
|
printf("[%" PRIu64 "] %" PRIu64 ", %" PRIu64 "\n",
|
|
off / store.blocksize, off, off + store.blocksize - 1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|