"Fossies" - the Fresh Open Source Software Archive

Member "xorriso-1.5.4/libjte/checksum.c" (30 Jan 2021, 16633 Bytes) of package /linux/misc/xorriso-1.5.4.pl02.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "checksum.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5.2_vs_1.5.4.

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