ckiss

KISS in C
git clone git://bvnf.space/ckiss.git
Log | Files | Refs | README | LICENSE

checksums.c (2737B)


      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
#include <assert.h>
#include <blake3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utils.h"
#include "checksums.h"

/* reads f and returns the checksum */
static char *
file_checksum(FILE *f) {
    /* This function is essentially copied from b3sum:
     * https://git.sr.ht/~mcf/b3sum/tree/cb4111ccc8061039b014fbb657c72f78984f1069/item/b3sum.c#L19
     * which is used under CC0. */

    size_t outlen = 33, len;
    unsigned char *out = malloc(outlen);
    if (out == NULL)
        die_perror("malloc");

    size_t buflen = 0x4000;
    char *buf = malloc(buflen);
    if (buf == NULL)
        die_perror("malloc");

    blake3_hasher ctx;
    blake3_hasher_init(&ctx);

    do {
        len = fread(buf, 1, buflen, f);
        if (len > 0)
            blake3_hasher_update(&ctx, buf, len);
    } while (len == buflen);
    if (ferror(f))
        die_perror("fread");

    blake3_hasher_finalize(&ctx, out, outlen);
    free(buf);

    size_t hexlen = 2 * outlen;
    char *hex = malloc(hexlen + 1);
    if (hex == NULL)
        die_perror("malloc");
    for (size_t i = 0; i < outlen; i++)
        sprintf(hex + 2*i, "%02x", out[i]);
    hex[hexlen] = '\0';

    free(out);
    return hex;
}

/* returns the checksum of the file specified by s, if needed and if the cache
 * is present (must download first) */
char *
source_generate_checksum(struct source *s) {
    assert(s);
    if (s->type != SRC_HTTP && s->type != SRC_FILE)
        return NULL; /* checksum not needed */

    FILE *f = fopen(s->cachefile, "rb");
    if (f == NULL)
        die_perror(s->cachefile);

    char *c = file_checksum(f);
    fclose(f);
    return c;
}

/* dies if there is a checksum mismatch. */
void
verify_checksums(struct pkg *p) {
    assert(p);
    FILE *f = pkg_open_file(p->pkg_path, "checksums", "r");
    if (f == NULL) {
        if (p->n_need_checksums == 0)
            return;
        else
            die2(p->pkg, "checksums needed but no checksum file");
    }

    assert(p->s);

    char *buf = NULL;
    size_t bufn = 0;
    ssize_t n;
    for (size_t i = 0; i < p->n; i++) {
        if (p->s[i]->type != SRC_HTTP && p->s[i]->type != SRC_FILE)
            continue;

        if ((n = getline(&buf, &bufn, f)) == -1) {
            free(buf);
            fclose(f);
            perror(NULL);
            die2(p->s[i]->remote, "checksums missing");
        }
        if (buf[n - 1] == '\n')
            buf[--n] = '\0';

        char *sum = source_generate_checksum(p->s[i]);
        if (strcmp(buf, sum) != 0) {
            free(sum);
            free(buf);
            fclose(f);
            die2(p->s[i]->cachefile, "checksum mismatch");
        }
        free(sum);
    }
    free(buf);
    fclose(f);
}