diff options
Diffstat (limited to '')
-rw-r--r-- | check.c | 282 |
1 files changed, 282 insertions, 0 deletions
@@ -0,0 +1,282 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +union str_or_off { + const char *s; + size_t off; +}; + +struct listing { + union str_or_off algorithm; + union str_or_off filename; /* first char determines mode */ + union str_or_off checksum; +}; + + +static int +listingcmp(const void *av, const void *bv) +{ + const struct listing *a = av, *b = bv; + int r = strcmp(a->filename.s, b->filename.s); + if (r) + return r; + return strcmp(a->algorithm.s, b->algorithm.s); +} + + +static int +parse_listing(struct listing **listingp, size_t *listing_lenp, size_t *listing_sizep, + struct buffer *buffer, const char *fname, int warn_improper_format, enum format format) +{ + size_t lineno = 1U; + char end = (format & WITH_NUL) ? '\0' : '\n'; + + for (; buffer->procoff < buffer->offset; lineno++) { + for (; buffer->procoff < buffer->offset; buffer->procoff++) + if (buffer->buf[buffer->procoff] == end) + break; + if (buffer->procoff == buffer->offset) { + weprintf("last line in \"%s\" is not terminated", fname); + if (!warn_improper_format) + return -1; + if (buffer->offset == buffer->size) + buffer->buf = erealloc(buffer->buf, buffer->size += 1U); + } + buffer->buf[buffer->procoff++] = '\0'; + + if (*listing_lenp == *listing_sizep) + *listingp = ereallocarray(*listingp, *listing_sizep += 512U, sizeof(**listingp)); + + (*listingp)[*listing_lenp].algorithm.off = buffer->ready; + if (format & WITH_ALGOSTR) { + for (; buffer->buf[buffer->ready] != ':'; buffer->ready++) + if (isspace(buffer->buf[buffer->ready]) || !buffer->buf[buffer->ready]) + goto badline; + buffer->buf[buffer->ready++] = '\0'; + } + + (*listingp)[*listing_lenp].checksum.off = buffer->ready; + for (; isxdigit(buffer->buf[buffer->ready]); buffer->ready++) + buffer->buf[buffer->ready] = (char)tolower(buffer->buf[buffer->ready]); + if (buffer->buf[buffer->ready] != ' ') + goto badline; + buffer->buf[buffer->ready++] = '\0'; + + if (buffer->buf[buffer->ready] != ' ' && + buffer->buf[buffer->ready] != '*') + goto badline; + if (buffer->buf[buffer->ready] == '*') + buffer->buf[buffer->ready] = ' '; /* binary is insignificant */ + (*listingp)[*listing_lenp].filename.off = buffer->ready; + + if (!buffer->buf[(*listingp)[*listing_lenp].algorithm.off] || + !buffer->buf[(*listingp)[*listing_lenp].checksum.off] || + !buffer->buf[(*listingp)[*listing_lenp].filename.off + 1U]) + goto badline; + + *listing_lenp += 1U; +nextline: + buffer->ready = buffer->procoff; + } + + return 0; + +badline: + weprintf("line %zu in \"%s\" is improperly formatted", lineno, fname); + if (warn_improper_format) + goto nextline; + return -1; +} + + +static int +get_listing(char **files, struct listing **listingp, size_t *listing_lenp, size_t *listing_sizep, + struct buffer *buffer, struct algorithm *algorithms, size_t nalgorithms, + enum format format, int warn_improper_format) +{ + const char *fname; + int r, fd, is_new_fd; + size_t i; + + format &= (enum format)~FORMAT_MASK; + format |= LOWERCASE_HEX; + + buffer->procoff = 0; + for (; *files; files++) { + fd = openfile(*files, &is_new_fd, &fname); + if (fd < 0) + return -1; + + while (!(r = feedbuffer(fd, buffer, fname))); + if (r < 0) + goto fail; + + if (is_new_fd) { + close(fd); + is_new_fd = 0; + } + + if (parse_listing(listingp, listing_lenp, listing_sizep, buffer, + fname, warn_improper_format, format)) + goto fail; + } + + for (i = 0; i < *listing_lenp; i++) { + (*listingp)[i].algorithm.s = &buffer->buf[(*listingp)[i].algorithm.off]; + (*listingp)[i].filename.s = &buffer->buf[(*listingp)[i].filename.off]; + (*listingp)[i].checksum.s = &buffer->buf[(*listingp)[i].checksum.off]; + } + if (!(format & WITH_ALGOSTR)) { + if (nalgorithms != 1U) + abort(); + for (i = 0; i < *listing_lenp; i++) + (*listingp)[i].algorithm.s = algorithms[0].algostr; + } + + return 0; + +fail: + if (is_new_fd) + close(fd); + return -1; +} + + +static int +compare(const char *hash, size_t hash_len, const struct listing *listing, size_t nlisting, int *truncated_out) +{ + int truncated, ok = 0; + size_t len; + *truncated_out = 0; + for (; nlisting--; listing++) { + len = strlen(listing->checksum.s); + truncated = len < hash_len; + len = MIN(hash_len, len); + if (!strncmp(hash, listing->checksum.s, len)) { + ok = 1; + if (!truncated) + return 1; + } + } + *truncated_out = 1; + return ok; +} + + +static int +check_file(struct listing *listing, size_t nlisting, size_t nthreads, struct global_data *global, + struct barrier_group *group, size_t *group_size) +{ + size_t i, j, n, orig_count, nthreads_cur, nthreads_now; + int cmp, truncated; + + memset(global->algorithms, 0, nlisting * sizeof(*global->algorithms)); + global->algorithms[0].algostr = listing[0].algorithm.s; + global->algorithms[0].result = NULL; + global->nalgorithms = 1U; + for (i = 1U; i < nlisting; i++) { + if (!strcmp(listing[i].algorithm.s, listing[i - 1U].algorithm.s)) { + listing[i].algorithm.s = listing[i - 1U].algorithm.s; + } else { + global->algorithms[global->nalgorithms].algostr = listing[i].algorithm.s; + global->algorithms[global->nalgorithms].result = NULL; + global->nalgorithms++; + } + } + + nthreads_now = nthreads ? nthreads : getautonthreads(); + nthreads_cur = MAX(MIN(global->nalgorithms, nthreads_now), 1U); + if (nthreads_cur > *group_size) { + if (*group_size) + killbarriergroup(group, global); + createbarriergroup(group, nthreads_cur, global); + } + + orig_count = global->nalgorithms; + if (calculate(global->file, group, global)) { + printf("%s: %s\n", global->file, errno == ENOENT ? "Missing" : "Error"); + return errno == ENOENT ? 1 : 2; + } + + for (i = 0, j = 0; i < global->nalgorithms; i++, j += n) { + n = 1U; + for (; n < j - nlisting; n++) + if (listing[j].algorithm.s != listing[j + n].algorithm.s) + break; + cmp = strcmp(global->algorithms[0].algostr, listing[j].algorithm.s); + if (cmp < 0) + abort(); + else if (cmp > 0) + continue; + if (compare(global->algorithms[0].result, global->algorithms[0].result_length, &listing[j], n, &truncated)) { + printf("%s: OK%s\n", global->file, truncated ? " (calculated hash was shorted the listed checksum)" : ""); + return 0; + } + } + + if (global->nalgorithms == orig_count) { + printf("%s: Fail\n", global->file); + return 1; + } else if (global->nalgorithms) { + printf("%s: Fail (some hash functions were not supported)\n", global->file); + return 2; + } else { + printf("%s: Fail (no hash function was supported)\n", global->file); + return 2; + } +} + + +int +verify_checksums(char **files, struct algorithm *algorithms, size_t nalgorithms, size_t nthreads, + enum format format, int warn_improper_format, int hexinput) +{ + struct listing *listing = NULL; + size_t listing_len = 0; + size_t listing_size = 0; + struct buffer listbuffer = {0}; + struct buffer filebuffer = {0}; + size_t i, first, n; + int r, ret = 0; + struct global_data global; + size_t algorithms_size = 0; + struct barrier_group group; + size_t group_size = 0; + + if (get_listing(files, &listing, &listing_len, &listing_size, &listbuffer, + algorithms, nalgorithms, format, warn_improper_format)) + goto fail; + qsort(listing, listing_len, sizeof(*listing), &listingcmp); + + global.algorithms = NULL; + global.buffer = &filebuffer; + for (i = 0; i < listing_len;) { + first = i++; + while (i < listing_len && !strcmp(listing[i].filename.s, listing[first].filename.s)) + i++; + n = i - first; + if (n > algorithms_size) { + algorithms_size = n; + global.algorithms = ereallocarray(global.algorithms, n, sizeof(*global.algorithms)); + } + global.file = &listing[i].filename.s[1]; + global.hexinput = (hexinput || listing[i].filename.s[0] == '#'); + r = check_file(&listing[i], n, nthreads, &global, &group, &group_size); + if (r < 0) + goto fail; + ret = MAX(r, ret); + } + +out: + if (group_size) + killbarriergroup(&group, &global); + free(listing); + free(listbuffer.buf); + free(filebuffer.buf); + return ret; + +fail: + ret = 2; + goto out; +} |