cpdup(1): Convert to LibreSSL MD5 hash API.
[dragonfly.git] / bin / cpdup / md5.c
CommitLineData
577109ea
MD
1/*-
2 * MD5.C
3 *
4 * (c) Copyright 1997-1999 by Matthew Dillon and Dima Ruban. Permission to
5 * use and distribute based on the FreeBSD copyright. Supplied as-is,
6 * USE WITH EXTREME CAUTION.
577109ea
MD
7 */
8
9#include "cpdup.h"
10
de78d61c 11#ifdef WITH_LIBMD
12#include <md5.h>
13#else
14#include <openssl/md5.h>
15#endif
16
577109ea
MD
17typedef struct MD5Node {
18 struct MD5Node *md_Next;
19 char *md_Name;
20 char *md_Code;
21 int md_Accessed;
22} MD5Node;
23
24static MD5Node *md5_lookup(const char *sfile);
25static void md5_cache(const char *spath, int sdirlen);
d5fdcd00 26static char *doMD5File(const char *filename, char *buf, int is_target);
577109ea
MD
27
28static char *MD5SCache; /* cache source directory name */
29static MD5Node *MD5Base;
30static int MD5SCacheDirLen;
31static int MD5SCacheDirty;
32
d0a6ae34 33void
577109ea
MD
34md5_flush(void)
35{
698d0b11 36 if (MD5SCacheDirty && MD5SCache && NotForRealOpt == 0) {
577109ea
MD
37 FILE *fo;
38
39 if ((fo = fopen(MD5SCache, "w")) != NULL) {
40 MD5Node *node;
41
42 for (node = MD5Base; node; node = node->md_Next) {
43 if (node->md_Accessed && node->md_Code) {
d0a6ae34 44 fprintf(fo, "%s %zu %s\n",
45 node->md_Code,
577109ea
MD
46 strlen(node->md_Name),
47 node->md_Name
48 );
49 }
50 }
51 fclose(fo);
52 }
53 }
54
55 MD5SCacheDirty = 0;
56
57 if (MD5SCache) {
58 MD5Node *node;
59
60 while ((node = MD5Base) != NULL) {
61 MD5Base = node->md_Next;
62
63 if (node->md_Code)
64 free(node->md_Code);
65 if (node->md_Name)
66 free(node->md_Name);
67 free(node);
68 }
69 free(MD5SCache);
70 MD5SCache = NULL;
71 }
72}
73
74static void
75md5_cache(const char *spath, int sdirlen)
76{
77 FILE *fi;
78
79 /*
80 * Already cached
81 */
82
83 if (
84 MD5SCache &&
85 sdirlen == MD5SCacheDirLen &&
86 strncmp(spath, MD5SCache, sdirlen) == 0
87 ) {
88 return;
89 }
90
91 /*
92 * Different cache, flush old cache
93 */
94
95 if (MD5SCache != NULL)
96 md5_flush();
97
98 /*
99 * Create new cache
100 */
101
102 MD5SCacheDirLen = sdirlen;
103 MD5SCache = mprintf("%*.*s%s", sdirlen, sdirlen, spath, MD5CacheFile);
104
105 if ((fi = fopen(MD5SCache, "r")) != NULL) {
106 MD5Node **pnode = &MD5Base;
107 int c;
108
109 c = fgetc(fi);
110 while (c != EOF) {
111 MD5Node *node = *pnode = malloc(sizeof(MD5Node));
112 char *s;
113 int nlen;
114
115 nlen = 0;
116
117 if (pnode == NULL || node == NULL) {
118 fprintf(stderr, "out of memory\n");
119 exit(EXIT_FAILURE);
120 }
121
122 bzero(node, sizeof(MD5Node));
123 node->md_Code = fextract(fi, -1, &c, ' ');
124 node->md_Accessed = 1;
125 if ((s = fextract(fi, -1, &c, ' ')) != NULL) {
126 nlen = strtol(s, NULL, 0);
127 free(s);
128 }
129 /*
d0a6ae34 130 * extracting md_Name - name may contain embedded control
577109ea
MD
131 * characters.
132 */
d5fdcd00 133 CountSourceReadBytes += nlen+1;
577109ea
MD
134 node->md_Name = fextract(fi, nlen, &c, EOF);
135 if (c != '\n') {
136 fprintf(stderr, "Error parsing MD5 Cache: %s (%c)\n", MD5SCache, c);
137 while (c != EOF && c != '\n')
138 c = fgetc(fi);
139 }
140 if (c != EOF)
141 c = fgetc(fi);
142 pnode = &node->md_Next;
143 }
144 fclose(fi);
145 }
146}
147
148/*
149 * md5_lookup: lookup/create md5 entry
150 */
151
152static MD5Node *
153md5_lookup(const char *sfile)
154{
155 MD5Node **pnode;
156 MD5Node *node;
157
158 for (pnode = &MD5Base; (node = *pnode) != NULL; pnode = &node->md_Next) {
159 if (strcmp(sfile, node->md_Name) == 0) {
160 break;
161 }
162 }
163 if (node == NULL) {
164
165 if ((node = *pnode = malloc(sizeof(MD5Node))) == NULL) {
166 fprintf(stderr,"out of memory\n");
167 exit(EXIT_FAILURE);
168 }
169
170 bzero(node, sizeof(MD5Node));
171 node->md_Name = strdup(sfile);
172 }
173 node->md_Accessed = 1;
174 return(node);
175}
176
177/*
178 * md5_check: check MD5 against file
179 *
180 * Return -1 if check failed
181 * Return 0 if check succeeded
182 *
183 * dpath can be NULL, in which case we are force-updating
184 * the source MD5.
185 */
186int
187md5_check(const char *spath, const char *dpath)
188{
189 const char *sfile;
190 char *dcode;
191 int sdirlen;
192 int r;
193 MD5Node *node;
194
195 r = -1;
196
197 if ((sfile = strrchr(spath, '/')) != NULL)
198 ++sfile;
199 else
200 sfile = spath;
201 sdirlen = sfile - spath;
202
203 md5_cache(spath, sdirlen);
204
205 node = md5_lookup(sfile);
206
207 /*
208 * If dpath == NULL, we are force-updating the source .MD5* files
209 */
210
211 if (dpath == NULL) {
d5fdcd00 212 char *scode = doMD5File(spath, NULL, 0);
577109ea
MD
213
214 r = 0;
215 if (node->md_Code == NULL) {
216 r = -1;
217 node->md_Code = scode;
218 MD5SCacheDirty = 1;
219 } else if (strcmp(scode, node->md_Code) != 0) {
220 r = -1;
221 free(node->md_Code);
222 node->md_Code = scode;
223 MD5SCacheDirty = 1;
224 } else {
225 free(scode);
226 }
227 return(r);
228 }
229
230 /*
231 * Otherwise the .MD5* file is used as a cache.
232 */
233
234 if (node->md_Code == NULL) {
d5fdcd00 235 node->md_Code = doMD5File(spath, NULL, 0);
577109ea
MD
236 MD5SCacheDirty = 1;
237 }
238
d5fdcd00 239 dcode = doMD5File(dpath, NULL, 1);
577109ea
MD
240 if (dcode) {
241 if (strcmp(node->md_Code, dcode) == 0) {
242 r = 0;
243 } else {
d5fdcd00 244 char *scode = doMD5File(spath, NULL, 0);
577109ea
MD
245
246 if (strcmp(node->md_Code, scode) == 0) {
247 free(scode);
248 } else {
249 free(node->md_Code);
250 node->md_Code = scode;
251 MD5SCacheDirty = 1;
252 if (strcmp(node->md_Code, dcode) == 0)
253 r = 0;
254 }
255 }
256 free(dcode);
257 }
258 return(r);
259}
260
de78d61c 261#ifndef WITH_LIBMD
262static char *
263md5_file(const char *filename, char *buf)
264{
265 unsigned char digest[MD5_DIGEST_LENGTH];
266 static const char hex[]="0123456789abcdef";
267 MD5_CTX ctx;
268 unsigned char buffer[4096];
269 struct stat st;
270 off_t size;
271 int fd, bytes, i;
272
273 fd = open(filename, O_RDONLY);
274 if (fd < 0)
275 return NULL;
276 if (fstat(fd, &st) < 0) {
277 bytes = -1;
278 goto err;
279 }
280
281 MD5_Init(&ctx);
282 size = st.st_size;
283 bytes = 0;
284 while (size > 0) {
285 if ((size_t)size > sizeof(buffer))
286 bytes = read(fd, buffer, sizeof(buffer));
287 else
288 bytes = read(fd, buffer, size);
289 if (bytes < 0)
290 break;
291 MD5_Update(&ctx, buffer, bytes);
292 size -= bytes;
293 }
294
295err:
296 close(fd);
297 if (bytes < 0)
298 return NULL;
299
300 if (!buf)
301 buf = malloc(MD5_DIGEST_LENGTH * 2 + 1);
302 if (!buf)
303 return NULL;
304
305 MD5_Final(digest, &ctx);
306 for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
307 buf[2*i] = hex[digest[i] >> 4];
308 buf[2*i+1] = hex[digest[i] & 0x0f];
309 }
310 buf[MD5_DIGEST_LENGTH * 2] = '\0';
311
312 return buf;
313}
314#endif
315
577109ea 316char *
d5fdcd00 317doMD5File(const char *filename, char *buf, int is_target)
577109ea
MD
318{
319 if (SummaryOpt) {
320 struct stat st;
321 if (stat(filename, &st) == 0) {
c0538630 322 uint64_t size = st.st_size;
d5fdcd00
MD
323 if (is_target)
324 CountTargetReadBytes += size;
325 else
326 CountSourceReadBytes += size;
577109ea
MD
327 }
328 }
de78d61c 329#ifdef WITH_LIBMD
577109ea 330 return MD5File(filename, buf);
de78d61c 331#else
332 return md5_file(filename, buf);
333#endif
577109ea
MD
334}
335