aboutsummaryrefslogtreecommitdiffstats
path: root/text_cmds/md5/commoncrypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'text_cmds/md5/commoncrypto.c')
-rw-r--r--text_cmds/md5/commoncrypto.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/text_cmds/md5/commoncrypto.c b/text_cmds/md5/commoncrypto.c
new file mode 100644
index 0000000..3551afe
--- /dev/null
+++ b/text_cmds/md5/commoncrypto.c
@@ -0,0 +1,108 @@
+/* Generic CommonDigest wrappers to match the semantics of libmd. */
+
+#include <dispatch/dispatch.h>
+#include <os/assumes.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include "commoncrypto.h"
+
+#define CHUNK_SIZE (10 * 1024 * 1024)
+
+char *
+Digest_End(CCDigestRef ctx, char *buf)
+{
+ static const char hex[] = "0123456789abcdef";
+ uint8_t digest[32]; // SHA256 is the biggest
+ size_t i, length;
+
+ (void)os_assumes_zero(CCDigestFinal(ctx, digest));
+ length = CCDigestOutputSize(ctx);
+ os_assert(length <= sizeof(digest));
+ for (i = 0; i < length; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+ return buf;
+}
+
+char *
+Digest_Data(CCDigestAlg algorithm, const void *data, size_t len, char *buf)
+{
+ CCDigestCtx ctx;
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, data, len));
+ return Digest_End(&ctx, buf);
+}
+
+char *
+Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
+{
+ int fd;
+ __block CCDigestCtx ctx;
+ dispatch_queue_t queue;
+ dispatch_semaphore_t sema;
+ dispatch_io_t io;
+ __block int s_error = 0;
+ __block bool eof = false;
+ off_t chunk_offset;
+
+ /* dispatch_io_create_with_path requires an absolute path */
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ (void)fcntl(fd, F_NOCACHE, 1);
+
+ (void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
+
+ queue = dispatch_queue_create("com.apple.mtree.io", NULL);
+ os_assert(queue);
+ sema = dispatch_semaphore_create(0);
+ os_assert(sema);
+
+ io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
+ if (error != 0) {
+ s_error = error;
+ }
+ (void)close(fd);
+ (void)dispatch_semaphore_signal(sema);
+ });
+ os_assert(io);
+ for (chunk_offset = 0; eof == false && s_error == 0; chunk_offset += CHUNK_SIZE) {
+ dispatch_io_read(io, chunk_offset, CHUNK_SIZE, queue, ^(bool done, dispatch_data_t data, int error) {
+ if (data != NULL) {
+ (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
+ (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
+ return (bool)true;
+ });
+ }
+
+ if (error != 0) {
+ s_error = error;
+ }
+
+ if (done) {
+ eof = (data == dispatch_data_empty);
+ dispatch_semaphore_signal(sema);
+ }
+ });
+ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+ }
+ dispatch_release(io); // it will close on its own
+
+ (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+
+ dispatch_release(queue);
+ dispatch_release(sema);
+
+ if (s_error != 0) {
+ errno = s_error;
+ return NULL;
+ }
+
+ return Digest_End(&ctx, buf);
+}