1#include <dispatch/dispatch.h>
2#include <os/assumes.h>
3#include <errno.h>
4#include <fcntl.h>
5
6#include "commoncrypto.h"
7
8/* Generic version of libmd's *_File() functions. */
9char *
10Digest_File(CCDigestAlg algorithm, const char *filename, char *buf)
11{
12	static const char hex[] = "0123456789abcdef";
13	int fd;
14	__block CCDigestCtx ctx;
15	dispatch_queue_t queue;
16	dispatch_semaphore_t sema;
17	dispatch_io_t io;
18	__block int s_error = 0;
19	uint8_t digest[32]; // SHA256 is the biggest
20	size_t i, length;
21
22	/* dispatch_io_create_with_path requires an absolute path */
23	fd = open(filename, O_RDONLY);
24	if (fd < 0) {
25		return NULL;
26	}
27
28	(void)fcntl(fd, F_NOCACHE, 1);
29
30	(void)os_assumes_zero(CCDigestInit(algorithm, &ctx));
31
32	queue = dispatch_queue_create("com.apple.mtree.io", NULL);
33	os_assert(queue);
34	sema = dispatch_semaphore_create(0);
35	os_assert(sema);
36
37	io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {
38		if (error != 0) {
39			s_error = error;
40		}
41		(void)close(fd);
42		(void)dispatch_semaphore_signal(sema);
43	});
44	os_assert(io);
45	dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) {
46		if (data != NULL) {
47			(void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) {
48				(void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size));
49				return (bool)true;
50			});
51		}
52
53		if (error != 0) {
54			s_error = error;
55		}
56	});
57	dispatch_release(io); // it will close on its own
58
59	(void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
60
61	dispatch_release(queue);
62	dispatch_release(sema);
63
64	if (s_error != 0) {
65		errno = s_error;
66		return NULL;
67	}
68
69	/* Finalize and convert to hex. */
70	(void)os_assumes_zero(CCDigestFinal(&ctx, digest));
71	length = CCDigestOutputSize(&ctx);
72	os_assert(length <= sizeof(digest));
73	for (i = 0; i < length; i++) {
74		buf[i+i] = hex[digest[i] >> 4];
75		buf[i+i+1] = hex[digest[i] & 0x0f];
76	}
77	buf[i+i] = '\0';
78
79	return buf;
80}
81