Code cleanups after running with "sparse"
[jigit.git] / libjte / checksum.c
1 /*
2  * checksum.c
3  *
4  * Copyright (c) 2008- Steve McIntyre <steve@einval.com>
5  *
6  * Implementation of a generic checksum interface, used in JTE.
7  *
8  * GNU GPL v2+
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "../config.h"
13 #endif
14
15 #include <sys/types.h>
16 #include <regex.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20
21 #ifdef HAVE_STDINT_H
22 #include <stdint.h>
23 #else
24 #ifdef HAVE_INTTYPES_H
25 #include <inttypes.h>
26 #endif
27 #endif
28
29 #include "md5.h"
30 #include "sha1.h"
31 #include "sha256.h"
32 #include "sha512.h"
33 #include "checksum.h"
34
35 #ifdef THREADED_CHECKSUMS
36 #include <pthread.h>
37 #endif
38
39 static void md5_init(void *context)
40 {
41     mk_MD5Init(context);
42 }
43 static void md5_update(void *context, unsigned char const *buf, unsigned int len)
44 {
45     mk_MD5Update(context, buf, len);
46 }
47 static void md5_final(unsigned char *digest, void *context)
48 {
49     mk_MD5Final(digest, context);
50 }
51
52 static void sha1_init(void *context)
53 {
54     sha1_init_ctx(context);
55 }
56 static void sha1_update(void *context, unsigned char const *buf, unsigned int len)
57 {
58     sha1_write(context, buf, len);
59 }
60 static void sha1_final(unsigned char *digest, void *context)
61 {
62     sha1_finish_ctx(context);
63     memcpy(digest, sha1_read(context), 20);
64 }
65
66 static void sha256_init(void *context)
67 {
68     sha256_init_ctx(context);
69 }
70 static void sha256_update(void *context, unsigned char const *buf, unsigned int len)
71 {
72     sha256_process_bytes(buf, len, context);
73 }
74 static void sha256_final(unsigned char *digest, void *context)
75 {
76     sha256_finish_ctx(context, digest);
77 }
78
79 static void sha512_init(void *context)
80 {
81     sha512_init_ctx(context);
82 }
83 static void sha512_update(void *context, unsigned char const *buf, unsigned int len)
84 {
85     sha512_process_bytes(buf, len, context);
86 }
87 static void sha512_final(unsigned char *digest, void *context)
88 {
89     sha512_finish_ctx(context, digest);
90 }
91
92 struct checksum_details
93 {
94     char          *name;
95     char          *prog;
96     int            digest_size;
97     int            context_size;
98     void          (*init)(void *context);
99     void          (*update)(void *context, unsigned char const *buf, unsigned int len);
100     void          (*final)(unsigned char *digest, void *context);
101     int           check_used_value;
102 };
103
104 static const struct checksum_details algorithms[] = 
105 {
106     {
107         "MD5",
108         "md5sum",
109         16,
110         sizeof(struct mk_MD5Context),
111         md5_init,
112         md5_update,
113         md5_final,
114         CHECK_MD5_USED
115     },
116     {
117         "SHA1",
118         "sha1sum",
119         20,
120         sizeof(SHA1_CONTEXT),
121         sha1_init,
122         sha1_update,
123         sha1_final,
124         CHECK_SHA1_USED
125     },
126     {
127         "SHA256",
128         "sha256sum",
129         32,
130         sizeof(struct sha256_ctx),
131         sha256_init,
132         sha256_update,
133         sha256_final,
134         CHECK_SHA256_USED
135     },
136     {
137         "SHA512",
138         "sha512sum",
139         64,
140         sizeof(struct sha512_ctx),
141         sha512_init,
142         sha512_update,
143         sha512_final,
144         CHECK_SHA512_USED
145     }
146 };
147
148 struct algo_context
149 {
150     void                     *context;
151     unsigned char            *digest;
152     int                       enabled;
153     int                       finalised;
154     char                     *hexdump;
155 #ifdef THREADED_CHECKSUMS
156     unsigned char const      *buf;
157     unsigned int              len;
158     int                       which;
159     pthread_t                 thread;
160     struct _checksum_context *parent;
161     pthread_mutex_t           start_mutex;
162     pthread_cond_t            start_cv;
163 #endif
164 };
165
166 struct _checksum_context
167 {
168 #ifdef THREADED_CHECKSUMS
169     unsigned int           index;
170     unsigned int           threads_running;
171     unsigned int           threads_desired;
172     pthread_mutex_t        done_mutex;
173     pthread_cond_t         done_cv;
174 #endif
175     char                  *owner;
176     struct algo_context    algo[NUM_CHECKSUMS];
177 };
178
179 struct checksum_info *checksum_information(enum checksum_types which)
180 {
181     return (struct checksum_info *)&algorithms[which];
182 }
183
184 /* Dump a buffer in hex */
185 static void hex_dump_to_buffer(char *output_buffer, unsigned char *buf, size_t buf_size)
186 {
187     unsigned int i;
188     char *p = output_buffer;
189
190     memset(output_buffer, 0, 1 + (2*buf_size));
191     for (i = 0; i < buf_size ; i++)
192         p += sprintf(p, "%2.2x", buf[i]);
193 }
194
195 #ifdef THREADED_CHECKSUMS
196 static void *checksum_thread(void *arg)
197 {
198     struct algo_context *a = arg;
199     struct _checksum_context *c = a->parent;
200     int num_blocks_summed = 0;
201
202     while (1)
203     {
204         /* wait to be given some work to do */
205         pthread_mutex_lock(&a->start_mutex);
206         while (a->buf == NULL)
207         {
208             pthread_cond_wait(&a->start_cv, &a->start_mutex);
209         }
210         pthread_mutex_unlock(&a->start_mutex);
211
212         /* if we're given a zero-length buffer, then that means we're
213          * done */
214         if (a->len == 0)
215             break;
216
217         /* actually do the checksum on the supplied buffer */
218         algorithms[a->which].update(a->context, a->buf, a->len);
219         num_blocks_summed++;
220         a->buf = NULL;
221
222         /* and tell the main thread that we're done with that
223          * buffer */
224         pthread_mutex_lock(&c->done_mutex);
225         c->threads_running--;
226         if (c->threads_running == 0)
227             pthread_cond_signal(&c->done_cv);
228         pthread_mutex_unlock(&c->done_mutex);
229     }
230
231     pthread_exit(NULL);
232 }
233 #endif
234
235 checksum_context_t *checksum_init_context(int checksums, const char *owner)
236 {
237     int i = 0;
238 #ifdef THREADED_CHECKSUMS
239     int ret = 0;
240 #endif
241
242     struct _checksum_context *context = calloc(1, sizeof(struct _checksum_context));
243
244     if (!context)
245         return NULL;
246
247     context->owner = strdup(owner);
248     if (!context->owner)
249     {
250         free(context);
251         return NULL;
252     }   
253
254 #ifdef THREADED_CHECKSUMS
255     pthread_mutex_init(&context->done_mutex, NULL);
256     pthread_cond_init(&context->done_cv, NULL);
257     context->index = 0;
258     context->threads_running = 0;
259     context->threads_desired = 0;
260
261     for (i = 0; i < NUM_CHECKSUMS; i++)
262         if ( (1 << i) & checksums)
263             context->threads_desired++;    
264 #endif
265
266     for (i = 0; i < NUM_CHECKSUMS; i++)
267     {
268         struct algo_context *a = &context->algo[i];
269         if ( (1 << i) & checksums)
270         {
271             a->context = malloc(algorithms[i].context_size);
272             if (!a->context)
273             {
274                 checksum_free_context(context);
275                 return NULL;
276             }
277             a->digest = malloc(algorithms[i].digest_size);
278             if (!a->digest)
279             {
280                 checksum_free_context(context);
281                 return NULL;
282             }
283             a->hexdump = malloc(1 + (2*algorithms[i].digest_size));
284             if (!a->hexdump)
285             {
286                 checksum_free_context(context);
287                 return NULL;
288             }
289             algorithms[i].init(a->context);
290             a->enabled = 1;
291             a->finalised = 0;
292 #ifdef THREADED_CHECKSUMS
293             a->which = i;
294             a->parent = context;
295             a->buf = NULL;
296             a->len = 0;
297             pthread_mutex_init(&a->start_mutex, NULL);
298             pthread_cond_init(&a->start_cv, NULL);
299             ret = pthread_create(&a->thread, NULL, checksum_thread, a);
300             if (ret != 0)
301             {
302                 /* libjte issues an own message:
303                   fprintf(stderr, "failed to create new thread: %d\n", ret);
304                 */
305                 checksum_free_context(context);
306                 return NULL;
307             }
308 #endif
309         }
310         else
311             a->enabled = 0;
312     }
313     
314     return context;
315 }
316
317 void checksum_free_context(checksum_context_t *context)
318 {
319     int i = 0;
320     struct _checksum_context *c = context;
321
322     for (i = 0; i < NUM_CHECKSUMS; i++)
323     {
324         struct algo_context *a = &c->algo[i];
325
326 #ifdef THREADED_CHECKSUMS
327         if (a->thread)
328         {
329             void *ret;
330             pthread_cancel(a->thread);
331             pthread_join(a->thread, &ret);
332             a->thread = 0;
333         }
334 #endif
335         free(a->context);
336         free(a->digest);
337         free(a->hexdump);
338     }
339     free(c->owner);
340     free(c);
341 }
342
343 #ifdef THREADED_CHECKSUMS
344 void checksum_update(checksum_context_t *context,
345                      unsigned char const *buf, unsigned int len)
346 {
347     int i = 0;
348     struct _checksum_context *c = context;
349
350     /* >>> TODO : Find out for what purpose index shall serve.
351                   It is defined here and incremented. Not more.
352     */
353     static int index = 0;
354
355     index++;
356
357     c->threads_running = c->threads_desired;    
358     for (i = 0; i < NUM_CHECKSUMS; i++)
359     {
360         if (c->algo[i].enabled)
361         {
362             struct algo_context *a = &c->algo[i];
363             pthread_mutex_lock(&a->start_mutex);
364             a->len = len;
365             a->buf = buf;
366             pthread_cond_signal(&a->start_cv);
367             pthread_mutex_unlock(&a->start_mutex);
368         }
369     }
370
371     /* Should now all be running, wait on them all to return */
372     pthread_mutex_lock(&c->done_mutex);
373     while (c->threads_running > 0)
374     {
375         pthread_cond_wait(&c->done_cv, &c->done_mutex);
376     }
377     pthread_mutex_unlock(&c->done_mutex);
378 }
379
380 #else /* THREADED_CHECKSUMS */
381
382 void checksum_update(checksum_context_t *context,
383                      unsigned char const *buf, unsigned int len)
384 {
385     int i = 0;
386     struct _checksum_context *c = context;
387     
388     for (i = 0; i < NUM_CHECKSUMS; i++)
389     {
390         if (c->algo[i].enabled)
391         {
392             struct algo_context *a = &c->algo[i];
393             algorithms[i].update(a->context, buf, len);
394         }
395     }
396 }
397
398 #endif /* THREADED_CHECKSUMS */
399
400 void checksum_final(checksum_context_t *context)
401 {
402     int i = 0;
403     struct _checksum_context *c = context;
404     
405 #ifdef THREADED_CHECKSUMS
406     /* Clean up the threads */
407     c->threads_running = c->threads_desired;    
408
409     for (i = 0; i < NUM_CHECKSUMS; i++)
410     {
411         if (c->algo[i].enabled)
412         {
413             void *ret = NULL;
414             struct algo_context *a = &c->algo[i];
415
416             pthread_mutex_lock(&a->start_mutex);
417             a->len = 0;
418             a->buf = (unsigned char *)-1;
419             pthread_cond_signal(&a->start_cv);
420             pthread_mutex_unlock(&a->start_mutex);
421             pthread_join(a->thread, &ret);
422             a->thread = 0;
423         }
424     }
425 #endif
426
427     for (i = 0; i < NUM_CHECKSUMS; i++)
428     {
429         struct algo_context *a = &c->algo[i];
430         if (a->enabled)
431         {
432             algorithms[i].final(a->digest, a->context);
433             hex_dump_to_buffer(a->hexdump, a->digest, algorithms[i].digest_size);
434             a->finalised = 1;
435         }
436     }
437 }
438
439 void checksum_copy(checksum_context_t *context,
440                    enum checksum_types which,
441                    unsigned char *digest)
442 {
443     struct _checksum_context *c = context;
444
445     if (c->algo[which].enabled)
446     {
447         if (c->algo[which].finalised)
448             memcpy(digest, c->algo[which].digest, algorithms[which].digest_size);
449         else
450             memset(digest, 0, algorithms[which].digest_size);
451     }
452
453     else
454         /* >>> TODO : ??? Can this happen ? Why print and then go on ? */
455         fprintf(stderr, "Asked for %s checksum, not enabled!\n",
456                 algorithms[which].name);
457 }
458
459 const char *checksum_hex(checksum_context_t *context,
460                          enum checksum_types which)
461 {
462     struct _checksum_context *c = context;
463
464     if (c->algo[which].enabled && c->algo[which].finalised)
465         return c->algo[which].hexdump;
466
467     /* else */
468     return NULL;
469 }
470
471
472 /* Parse the command line options for which checksums to use */
473 int parse_checksum_algo(char *arg, int *algo)
474 {
475     int i = 0;
476     char *start_ptr = arg;
477     int len = 0;
478
479     (*algo) |= CHECK_MD5_USED; 
480
481     if (!strcasecmp(arg, "all"))
482     {
483         *algo = 0xFF;
484         return 0;
485     }
486     
487     while (*start_ptr != 0)
488     {
489         int match = 0;
490         len = 0;
491
492         while (start_ptr[len] != ',' && start_ptr[len] != 0)
493             len++;
494         
495         if (len)
496         {
497             for (i = 0; i < NUM_CHECKSUMS; i++)
498             {
499                 if (len == strlen(algorithms[i].name) &&
500                     !strncasecmp(start_ptr, algorithms[i].name, len))
501                 {
502                     match = 1;
503                     *algo |= algorithms[i].check_used_value;
504                 }
505             }
506         
507             if (!match)
508             {
509                 return EINVAL;
510             }
511         }
512         
513         if (start_ptr[len] == 0)
514             break;
515             
516         start_ptr += len + 1;
517     }
518    
519     return 0;
520 }
521
522 #ifdef CHECKSUM_SELF_TEST
523 #include <sys/types.h>
524 #include <sys/stat.h>
525 #include <fcntl.h>
526 #include <unistd.h>
527 #include <errno.h>
528 #include <stdlib.h>
529
530 int main(int argc, char **argv)
531 {
532     char buf[1024];
533     int fd = -1;
534     char *filename;
535     int err = 0;
536     static checksum_context_t *test_context = NULL;
537     int i = 0;
538
539     if (argc != 2)
540     {
541         fprintf(stderr, "Need a filename to act on!\n");
542         return 1;
543     }
544
545     filename = argv[1];
546     fd = open(filename, O_RDONLY);
547     if (fd < 0)
548     {
549         fprintf(stderr, "Unable to open file %s, errno %d\n", filename, errno);
550         return 1;
551     }
552
553     test_context = checksum_init_context(CHECK_ALL_USED, "test");
554     if (!test_context)
555     {
556         fprintf(stderr, "Unable to initialise checksum context\n");
557         close(fd);
558         return 1;
559     }
560
561     while(1)
562     {
563         err = read(fd, buf, sizeof(buf));
564         if (err < 0)
565         {
566             fprintf(stderr, "Failed to read from file, errno %d\n", errno);
567             return 1;
568         }
569
570         if (err == 0)
571             break; /* EOF */
572
573         /* else */
574         checksum_update(test_context, buf, err);
575     }
576     close(fd);
577     checksum_final(test_context);
578
579     for (i = 0; i < NUM_CHECKSUMS; i++)
580     {
581         struct checksum_info *info;
582         unsigned char r[64];
583         int j = 0;
584
585         info = checksum_information(i);
586         memset(r, 0, sizeof(r));
587
588         checksum_copy(test_context, i, r);
589
590         printf("OUR %s:\n", info->name);
591         for (j = 0; j < info->digest_size; j++)
592             printf("%2.2x", r[j]);
593         printf("  %s\n", filename);
594         printf("system checksum program (%s):\n", info->prog);
595         sprintf(buf, "%s %s", info->prog, filename);
596         system(buf);
597         printf("\n");
598     }
599     return 0;
600 }
601 #endif /* CHECKSUM_SELF_TEST */
602