aboutsummaryrefslogtreecommitdiffstats
path: root/check.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--check.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/check.c b/check.c
new file mode 100644
index 0000000..d014d65
--- /dev/null
+++ b/check.c
@@ -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;
+}