diff options
Diffstat (limited to 'common.c')
-rw-r--r-- | common.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/common.c b/common.c new file mode 100644 index 0000000..c389285 --- /dev/null +++ b/common.c @@ -0,0 +1,228 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +void * +erealloc(void *ptr, size_t n) +{ + ptr = realloc(ptr, n); + if (!ptr) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + exit(2); + } + return ptr; +} + +void * +emalloc(size_t n) +{ + void *ptr = malloc(n); + if (!ptr) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + exit(2); + } + return ptr; +} + +static int +parse_fd(const char *name) +{ + long int num; + char *end; + if (!isdigit(*name)) + return -1; + errno = 0; + num = strtol(name, &end, 10); + if (num > INT_MAX || *end || errno) + return -1; + return (int)num; +} + +int +open_file(const char *path, int *closep) +{ + int fd = -1; + + *closep = 0; + + if (!strcmp(path, "-")) + fd = STDIN_FILENO; + else if (!strcmp(path, "/dev/stdin")) + fd = STDIN_FILENO; + else if (!strcmp(path, "/dev/stdout")) + fd = STDOUT_FILENO; + else if (!strcmp(path, "/dev/stderr")) + fd = STDERR_FILENO; + else if (!strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1)) + fd = parse_fd(&path[sizeof("/dev/fd/") - 1]); + else if (!strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) + fd = parse_fd(&path[sizeof("/proc/self/fd/") - 1]); + + if (fd < 0) { + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + *closep = 1; + } + + return fd; +} + +static int +check_and_print_file(const char *path, size_t hashlen, int decode_hex, char *expected) +{ + unsigned char *hash; + int r, fd, close_fd; + + fd = open_file(path, &close_fd); + if (fd < 0) { + if (errno != ENOENT) + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + missing: + printf("%s: Missing\n", path); + return -1; + } + + hash = hashlen ? emalloc(hashlen / 8) : NULL; + r = hash_fd(fd, path, decode_hex, hash); + + if (close_fd) + close(fd); + + if (r < 0) { + free(hash); + goto missing; + } + + libblake_decode_hex(expected, hashlen / 4, expected, &(int){0}); + if (!memcmp(hash, expected, hashlen / 8)) { + free(hash); + printf("%s: OK\n", path); + return 0; + } else { + free(hash); + printf("%s: Fail\n", path); + return -1; + } +} + +int +check_and_print(const char *path, size_t hashlen, int decode_hex, char newline) +{ + int fd, close_fd, status = 0; + char *buf = NULL; + size_t size = 0; + size_t len = 0; + ssize_t r; + size_t i, j, k; + + fd = open_file(path, &close_fd); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + exit(2); + } + + for (;;) { + if (len == size) + buf = erealloc(buf, size += 8 << 10); + r = read(fd, &buf[len], size - len); + if (r > 0) { + len += (size_t)r; + } else if (!r) { + break; + } else if (errno == EINTR) { + continue; + } else { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + exit(2); + } + } + buf = erealloc(buf, len + 1); + buf[len] = '\0'; + + if (newline) { + for (i = 0; i < len; i = k + 1) { + while (isspace(buf[i])) + i++; + for (j = i; j - i < hashlen / 4; j++) + if (!isxdigit(buf[j])) + goto corrupt; + if (j == len || !isblank(buf[j])) + goto corrupt; + buf[j] = '\0'; + j++; + while (isblank(buf[j])) + j++; + if (!buf[j]) + goto corrupt; + for (k = j; buf[k] && buf[k] != newline;) + k++; + buf[k] = '\0'; + status |= check_and_print_file(&buf[j], hashlen, decode_hex, &buf[i]); + } + } else { + for (i = 0; i < len; i = k + 1) { + for (j = i; j - i < hashlen / 4; j++) + if (!isxdigit(buf[j])) + goto corrupt; + if (buf[j + 0] != ' ' || buf[j + 1] != ' ') + goto corrupt; + buf[j] = '\0'; + j += 2; + k = j + strlen(&buf[j]); + status |= check_and_print_file(&buf[j], hashlen, decode_hex, &buf[i]); + } + } + + if (close_fd) + close(fd); + return status; + +corrupt: + fprintf(stderr, "%s: %s: invalid file content\n", argv0, path); + exit(2); +} + +static int +hash_file(const char *path, int decode_hex, unsigned char hash[]) +{ + int ret, fd, close_fd; + + fd = open_file(path, &close_fd); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + return -1; + } + + ret = hash_fd(fd, path, decode_hex, hash); + + if (close_fd) + close(fd); + return ret; +} + +int +hash_and_print(const char *path, size_t hashlen, int decode_hex, char newline, int output_case) +{ + unsigned char *hash; + char *hex; + + hash = hashlen ? emalloc(hashlen / 8) : 0; + hex = emalloc(hashlen / 4 + 1); + + if (hash_file(path, decode_hex, hash)) { + free(hash); + free(hex); + return -1; + } + + if (output_case < 0) { + fwrite(hash, 1, hashlen / 8, stdout); + } else { + libblake_encode_hex(hash, hashlen / 8, hex, output_case); + printf("%s %s%c", hex, path, newline); + } + + free(hash); + free(hex); + return 0; +} |