4 * Copyright (c) 2008- Steve McIntyre <steve@einval.com>
6 * Implementation of a generic checksum interface, used in JTE.
12 #include "genisoimage.h"
25 #ifdef THREADED_CHECKSUMS
29 static void md5_init(void *context)
33 static void md5_update(void *context, unsigned char const *buf, unsigned int len)
35 mk_MD5Update(context, buf, len);
37 static void md5_final(unsigned char *digest, void *context)
39 mk_MD5Final(digest, context);
42 static void sha1_init(void *context)
44 sha1_init_ctx(context);
46 static void sha1_update(void *context, unsigned char const *buf, unsigned int len)
48 sha1_process_bytes(buf, len, context);
50 static void sha1_final(unsigned char *digest, void *context)
52 sha1_finish_ctx(context, digest);
55 static void sha256_init(void *context)
57 sha256_init_ctx(context);
59 static void sha256_update(void *context, unsigned char const *buf, unsigned int len)
61 sha256_process_bytes(buf, len, context);
63 static void sha256_final(unsigned char *digest, void *context)
65 sha256_finish_ctx(context, digest);
68 static void sha512_init(void *context)
70 sha512_init_ctx(context);
72 static void sha512_update(void *context, unsigned char const *buf, unsigned int len)
74 sha512_process_bytes(buf, len, context);
76 static void sha512_final(unsigned char *digest, void *context)
78 sha512_finish_ctx(context, digest);
81 struct checksum_details
87 void (*init)(void *context);
88 void (*update)(void *context, unsigned char const *buf, unsigned int len);
89 void (*final)(unsigned char *digest, void *context);
92 static const struct checksum_details algorithms[] =
98 sizeof(struct mk_MD5Context),
107 sizeof(struct sha1_ctx),
116 sizeof(struct sha256_ctx),
125 sizeof(struct sha512_ctx),
135 unsigned char *digest;
139 #ifdef THREADED_CHECKSUMS
140 unsigned char const *buf;
144 struct _checksum_context *parent;
145 pthread_mutex_t start_mutex;
146 pthread_cond_t start_cv;
150 struct _checksum_context
152 #ifdef THREADED_CHECKSUMS
154 unsigned int threads_running;
155 unsigned int threads_desired;
156 pthread_mutex_t done_mutex;
157 pthread_cond_t done_cv;
160 struct algo_context algo[NUM_CHECKSUMS];
163 struct checksum_info *checksum_information(enum checksum_types which)
165 return (struct checksum_info *)&algorithms[which];
168 /* Dump a buffer in hex */
169 static void hex_dump_to_buffer(char *output_buffer, unsigned char *buf, size_t buf_size)
172 char *p = output_buffer;
174 memset(output_buffer, 0, 1 + (2*buf_size));
175 for (i = 0; i < buf_size ; i++)
176 p += sprintf(p, "%2.2x", buf[i]);
179 #ifdef THREADED_CHECKSUMS
180 static void *checksum_thread(void *arg)
182 struct algo_context *a = arg;
183 struct _checksum_context *c = a->parent;
184 int num_blocks_summed = 0;
188 /* wait to be given some work to do */
189 pthread_mutex_lock(&a->start_mutex);
190 while (a->buf == NULL)
192 pthread_cond_wait(&a->start_cv, &a->start_mutex);
194 pthread_mutex_unlock(&a->start_mutex);
196 /* if we're given a zero-length buffer, then that means we're
201 /* actually do the checksum on the supplied buffer */
202 algorithms[a->which].update(a->context, a->buf, a->len);
206 /* and tell the main thread that we're done with that
208 pthread_mutex_lock(&c->done_mutex);
209 c->threads_running--;
210 if (c->threads_running == 0)
211 pthread_cond_signal(&c->done_cv);
212 pthread_mutex_unlock(&c->done_mutex);
219 checksum_context_t *checksum_init_context(int checksums, const char *owner)
223 struct _checksum_context *context = calloc(1, sizeof(struct _checksum_context));
228 context->owner = strdup(owner);
235 #ifdef THREADED_CHECKSUMS
236 pthread_mutex_init(&context->done_mutex, NULL);
237 pthread_cond_init(&context->done_cv, NULL);
239 context->threads_running = 0;
240 context->threads_desired = 0;
242 for (i = 0; i < NUM_CHECKSUMS; i++)
243 if ( (1 << i) & checksums)
244 context->threads_desired++;
247 for (i = 0; i < NUM_CHECKSUMS; i++)
249 struct algo_context *a = &context->algo[i];
250 if ( (1 << i) & checksums)
252 a->context = malloc(algorithms[i].context_size);
255 checksum_free_context(context);
258 a->digest = malloc(algorithms[i].digest_size);
261 checksum_free_context(context);
264 a->hexdump = malloc(1 + (2*algorithms[i].digest_size));
267 checksum_free_context(context);
270 algorithms[i].init(a->context);
273 #ifdef THREADED_CHECKSUMS
278 pthread_mutex_init(&a->start_mutex, NULL);
279 pthread_cond_init(&a->start_cv, NULL);
280 ret = pthread_create(&a->thread, NULL, checksum_thread, a);
283 fprintf(stderr, "failed to create new thread: %d\n", ret);
284 checksum_free_context(context);
296 void checksum_free_context(checksum_context_t *context)
299 struct _checksum_context *c = context;
301 for (i = 0; i < NUM_CHECKSUMS; i++)
303 struct algo_context *a = &c->algo[i];
305 #ifdef THREADED_CHECKSUMS
309 pthread_cancel(a->thread);
310 pthread_join(a->thread, &ret);
322 #ifdef THREADED_CHECKSUMS
323 void checksum_update(checksum_context_t *context,
324 unsigned char const *buf, unsigned int len)
327 struct _checksum_context *c = context;
328 static int index = 0;
332 c->threads_running = c->threads_desired;
333 for (i = 0; i < NUM_CHECKSUMS; i++)
335 if (c->algo[i].enabled)
337 struct algo_context *a = &c->algo[i];
338 pthread_mutex_lock(&a->start_mutex);
341 pthread_cond_signal(&a->start_cv);
342 pthread_mutex_unlock(&a->start_mutex);
346 /* Should now all be running, wait on them all to return */
347 pthread_mutex_lock(&c->done_mutex);
348 while (c->threads_running > 0)
350 pthread_cond_wait(&c->done_cv, &c->done_mutex);
352 pthread_mutex_unlock(&c->done_mutex);
355 #else // THREADED_CHECKSUMS
357 void checksum_update(checksum_context_t *context,
358 unsigned char const *buf, unsigned int len)
361 struct _checksum_context *c = context;
363 for (i = 0; i < NUM_CHECKSUMS; i++)
365 if (c->algo[i].enabled)
367 struct algo_context *a = &c->algo[i];
368 algorithms[i].update(a->context, buf, len);
373 #endif // THREADED_CHECKSUMS
375 void checksum_final(checksum_context_t *context)
378 struct _checksum_context *c = context;
380 #ifdef THREADED_CHECKSUMS
382 /* Clean up the threads */
383 c->threads_running = c->threads_desired;
385 for (i = 0; i < NUM_CHECKSUMS; i++)
387 if (c->algo[i].enabled)
390 struct algo_context *a = &c->algo[i];
392 pthread_mutex_lock(&a->start_mutex);
394 a->buf = (unsigned char *)-1;
395 pthread_cond_signal(&a->start_cv);
396 pthread_mutex_unlock(&a->start_mutex);
397 pthread_join(a->thread, &ret);
403 for (i = 0; i < NUM_CHECKSUMS; i++)
405 struct algo_context *a = &c->algo[i];
408 algorithms[i].final(a->digest, a->context);
409 hex_dump_to_buffer(a->hexdump, a->digest, algorithms[i].digest_size);
415 void checksum_copy(checksum_context_t *context,
416 enum checksum_types which,
417 unsigned char *digest)
419 struct _checksum_context *c = context;
421 if (c->algo[which].enabled)
423 if (c->algo[which].finalised)
424 memcpy(digest, c->algo[which].digest, algorithms[which].digest_size);
426 memset(digest, 0, algorithms[which].digest_size);
429 fprintf(stderr, "Asked for %s checksum, not enabled!\n",
430 algorithms[which].name);
433 const char *checksum_hex(checksum_context_t *context,
434 enum checksum_types which)
436 struct _checksum_context *c = context;
438 if (c->algo[which].enabled && c->algo[which].finalised)
439 return c->algo[which].hexdump;
446 /* Parse the command line options for which checksums to use */
447 int parse_checksum_algo(char *arg, int *algo)
451 char *start_ptr = arg;
456 if (!strcasecmp(arg, "all"))
462 while (*start_ptr != 0)
467 while (start_ptr[len] != ',' && start_ptr[len] != 0)
472 for (i = 0; i < NUM_CHECKSUMS; i++)
474 if (len == strlen(algorithms[i].name) &&
475 !strncasecmp(start_ptr, algorithms[i].name, len))
484 fprintf(stderr, "invalid algorithm name found in %s\n", arg);
489 if (start_ptr[len] == 0)
492 start_ptr += len + 1;
495 if (! (*algo & CHECK_MD5_USED))
497 fprintf(stderr, "invalid choices: algorithms *must* include MD5\n");
504 #ifdef CHECKSUM_SELF_TEST
505 #include <sys/types.h>
506 #include <sys/stat.h>
512 int main(int argc, char **argv)
518 static checksum_context_t *test_context = NULL;
523 fprintf(stderr, "Need a filename to act on!\n");
528 fd = open(filename, O_RDONLY);
531 fprintf(stderr, "Unable to open file %s, errno %d\n", filename, errno);
535 test_context = checksum_init_context(CHECK_ALL_USED, "test");
538 fprintf(stderr, "Unable to initialise checksum context\n");
544 err = read(fd, buf, sizeof(buf));
547 fprintf(stderr, "Failed to read from file, errno %d\n", errno);
555 checksum_update(test_context, buf, err);
558 checksum_final(test_context);
560 for (i = 0; i < NUM_CHECKSUMS; i++)
562 struct checksum_info *info;
566 info = checksum_information(i);
567 memset(r, 0, sizeof(r));
569 checksum_copy(test_context, i, r);
571 printf("OUR %s:\n", info->name);
572 for (j = 0; j < info->digest_size; j++)
573 printf("%2.2x", r[j]);
574 printf(" %s\n", filename);
575 printf("system checksum program (%s):\n", info->prog);
576 sprintf(buf, "%s %s", info->prog, filename);
582 #endif /* CHECKSUM_SELF_TEST */