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