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