xref: /dflybsd-src/bin/cpdup/md5.c (revision 3132c289c12a1670a758b19afc99c9449346c133)
1577109eaSMatthew Dillon /*-
2577109eaSMatthew Dillon  * MD5.C
3577109eaSMatthew Dillon  *
4577109eaSMatthew Dillon  * (c) Copyright 1997-1999 by Matthew Dillon and Dima Ruban.  Permission to
5577109eaSMatthew Dillon  *     use and distribute based on the FreeBSD copyright.  Supplied as-is,
6577109eaSMatthew Dillon  *     USE WITH EXTREME CAUTION.
7577109eaSMatthew Dillon  */
8577109eaSMatthew Dillon 
9577109eaSMatthew Dillon #include "cpdup.h"
10577109eaSMatthew Dillon 
11de78d61cSzrj #include <openssl/md5.h>
12de78d61cSzrj 
13577109eaSMatthew Dillon typedef struct MD5Node {
14577109eaSMatthew Dillon     struct MD5Node *md_Next;
15577109eaSMatthew Dillon     char *md_Name;
16577109eaSMatthew Dillon     char *md_Code;
17577109eaSMatthew Dillon     int md_Accessed;
18577109eaSMatthew Dillon } MD5Node;
19577109eaSMatthew Dillon 
20577109eaSMatthew Dillon static MD5Node *md5_lookup(const char *sfile);
21577109eaSMatthew Dillon static void md5_cache(const char *spath, int sdirlen);
22d5fdcd00SMatthew Dillon static char *doMD5File(const char *filename, char *buf, int is_target);
23577109eaSMatthew Dillon 
24577109eaSMatthew Dillon static char *MD5SCache;		/* cache source directory name */
25577109eaSMatthew Dillon static MD5Node *MD5Base;
26577109eaSMatthew Dillon static int MD5SCacheDirLen;
27577109eaSMatthew Dillon static int MD5SCacheDirty;
28577109eaSMatthew Dillon 
29577109eaSMatthew Dillon void
md5_flush(void)30577109eaSMatthew Dillon md5_flush(void)
31577109eaSMatthew Dillon {
32698d0b11SMatthew Dillon     if (MD5SCacheDirty && MD5SCache && NotForRealOpt == 0) {
33577109eaSMatthew Dillon 	FILE *fo;
34577109eaSMatthew Dillon 
35577109eaSMatthew Dillon 	if ((fo = fopen(MD5SCache, "w")) != NULL) {
36577109eaSMatthew Dillon 	    MD5Node *node;
37577109eaSMatthew Dillon 
38577109eaSMatthew Dillon 	    for (node = MD5Base; node; node = node->md_Next) {
39577109eaSMatthew Dillon 		if (node->md_Accessed && node->md_Code) {
40e823ea66SSascha Wildner 		    fprintf(fo, "%s %zu %s\n",
41577109eaSMatthew Dillon 			node->md_Code,
42577109eaSMatthew Dillon 			strlen(node->md_Name),
43577109eaSMatthew Dillon 			node->md_Name
44577109eaSMatthew Dillon 		    );
45577109eaSMatthew Dillon 		}
46577109eaSMatthew Dillon 	    }
47577109eaSMatthew Dillon 	    fclose(fo);
48577109eaSMatthew Dillon 	}
49577109eaSMatthew Dillon     }
50577109eaSMatthew Dillon 
51577109eaSMatthew Dillon     MD5SCacheDirty = 0;
52577109eaSMatthew Dillon 
53577109eaSMatthew Dillon     if (MD5SCache) {
54577109eaSMatthew Dillon 	MD5Node *node;
55577109eaSMatthew Dillon 
56577109eaSMatthew Dillon 	while ((node = MD5Base) != NULL) {
57577109eaSMatthew Dillon 	    MD5Base = node->md_Next;
58577109eaSMatthew Dillon 
59577109eaSMatthew Dillon 	    if (node->md_Code)
60577109eaSMatthew Dillon 		free(node->md_Code);
61577109eaSMatthew Dillon 	    if (node->md_Name)
62577109eaSMatthew Dillon 		free(node->md_Name);
63577109eaSMatthew Dillon 	    free(node);
64577109eaSMatthew Dillon 	}
65577109eaSMatthew Dillon 	free(MD5SCache);
66577109eaSMatthew Dillon 	MD5SCache = NULL;
67577109eaSMatthew Dillon     }
68577109eaSMatthew Dillon }
69577109eaSMatthew Dillon 
70577109eaSMatthew Dillon static void
md5_cache(const char * spath,int sdirlen)71577109eaSMatthew Dillon md5_cache(const char *spath, int sdirlen)
72577109eaSMatthew Dillon {
73577109eaSMatthew Dillon     FILE *fi;
74577109eaSMatthew Dillon 
75577109eaSMatthew Dillon     /*
76577109eaSMatthew Dillon      * Already cached
77577109eaSMatthew Dillon      */
78577109eaSMatthew Dillon 
79577109eaSMatthew Dillon     if (
80577109eaSMatthew Dillon 	MD5SCache &&
81577109eaSMatthew Dillon 	sdirlen == MD5SCacheDirLen &&
82577109eaSMatthew Dillon 	strncmp(spath, MD5SCache, sdirlen) == 0
83577109eaSMatthew Dillon     ) {
84577109eaSMatthew Dillon 	return;
85577109eaSMatthew Dillon     }
86577109eaSMatthew Dillon 
87577109eaSMatthew Dillon     /*
88577109eaSMatthew Dillon      * Different cache, flush old cache
89577109eaSMatthew Dillon      */
90577109eaSMatthew Dillon 
91577109eaSMatthew Dillon     if (MD5SCache != NULL)
92577109eaSMatthew Dillon 	md5_flush();
93577109eaSMatthew Dillon 
94577109eaSMatthew Dillon     /*
95577109eaSMatthew Dillon      * Create new cache
96577109eaSMatthew Dillon      */
97577109eaSMatthew Dillon 
98577109eaSMatthew Dillon     MD5SCacheDirLen = sdirlen;
99577109eaSMatthew Dillon     MD5SCache = mprintf("%*.*s%s", sdirlen, sdirlen, spath, MD5CacheFile);
100577109eaSMatthew Dillon 
101577109eaSMatthew Dillon     if ((fi = fopen(MD5SCache, "r")) != NULL) {
102577109eaSMatthew Dillon 	MD5Node **pnode = &MD5Base;
103577109eaSMatthew Dillon 	int c;
104577109eaSMatthew Dillon 
105577109eaSMatthew Dillon 	c = fgetc(fi);
106577109eaSMatthew Dillon 	while (c != EOF) {
107577109eaSMatthew Dillon 	    MD5Node *node = *pnode = malloc(sizeof(MD5Node));
108577109eaSMatthew Dillon 	    char *s;
109577109eaSMatthew Dillon 	    int nlen;
110577109eaSMatthew Dillon 
111577109eaSMatthew Dillon 	    nlen = 0;
112577109eaSMatthew Dillon 
113577109eaSMatthew Dillon 	    if (pnode == NULL || node == NULL) {
114577109eaSMatthew Dillon 		fprintf(stderr, "out of memory\n");
115577109eaSMatthew Dillon 		exit(EXIT_FAILURE);
116577109eaSMatthew Dillon 	    }
117577109eaSMatthew Dillon 
118577109eaSMatthew Dillon 	    bzero(node, sizeof(MD5Node));
119577109eaSMatthew Dillon 	    node->md_Code = fextract(fi, -1, &c, ' ');
120577109eaSMatthew Dillon 	    node->md_Accessed = 1;
121577109eaSMatthew Dillon 	    if ((s = fextract(fi, -1, &c, ' ')) != NULL) {
122577109eaSMatthew Dillon 		nlen = strtol(s, NULL, 0);
123577109eaSMatthew Dillon 		free(s);
124577109eaSMatthew Dillon 	    }
125577109eaSMatthew Dillon 	    /*
126577109eaSMatthew Dillon 	     * extracting md_Name - name may contain embedded control
127577109eaSMatthew Dillon 	     * characters.
128577109eaSMatthew Dillon 	     */
129d5fdcd00SMatthew Dillon 	    CountSourceReadBytes += nlen+1;
130577109eaSMatthew Dillon 	    node->md_Name = fextract(fi, nlen, &c, EOF);
131577109eaSMatthew Dillon 	    if (c != '\n') {
132577109eaSMatthew Dillon 		fprintf(stderr, "Error parsing MD5 Cache: %s (%c)\n", MD5SCache, c);
133577109eaSMatthew Dillon 		while (c != EOF && c != '\n')
134577109eaSMatthew Dillon 		    c = fgetc(fi);
135577109eaSMatthew Dillon 	    }
136577109eaSMatthew Dillon 	    if (c != EOF)
137577109eaSMatthew Dillon 		c = fgetc(fi);
138577109eaSMatthew Dillon 	    pnode = &node->md_Next;
139577109eaSMatthew Dillon 	}
140577109eaSMatthew Dillon 	fclose(fi);
141577109eaSMatthew Dillon     }
142577109eaSMatthew Dillon }
143577109eaSMatthew Dillon 
144577109eaSMatthew Dillon /*
145577109eaSMatthew Dillon  * md5_lookup:	lookup/create md5 entry
146577109eaSMatthew Dillon  */
147577109eaSMatthew Dillon 
148577109eaSMatthew Dillon static MD5Node *
md5_lookup(const char * sfile)149577109eaSMatthew Dillon md5_lookup(const char *sfile)
150577109eaSMatthew Dillon {
151577109eaSMatthew Dillon     MD5Node **pnode;
152577109eaSMatthew Dillon     MD5Node *node;
153577109eaSMatthew Dillon 
154577109eaSMatthew Dillon     for (pnode = &MD5Base; (node = *pnode) != NULL; pnode = &node->md_Next) {
155577109eaSMatthew Dillon 	if (strcmp(sfile, node->md_Name) == 0) {
156577109eaSMatthew Dillon 	    break;
157577109eaSMatthew Dillon 	}
158577109eaSMatthew Dillon     }
159577109eaSMatthew Dillon     if (node == NULL) {
160577109eaSMatthew Dillon 
161577109eaSMatthew Dillon 	if ((node = *pnode = malloc(sizeof(MD5Node))) == NULL) {
162577109eaSMatthew Dillon 		fprintf(stderr,"out of memory\n");
163577109eaSMatthew Dillon 		exit(EXIT_FAILURE);
164577109eaSMatthew Dillon 	}
165577109eaSMatthew Dillon 
166577109eaSMatthew Dillon 	bzero(node, sizeof(MD5Node));
167577109eaSMatthew Dillon 	node->md_Name = strdup(sfile);
168577109eaSMatthew Dillon     }
169577109eaSMatthew Dillon     node->md_Accessed = 1;
170577109eaSMatthew Dillon     return(node);
171577109eaSMatthew Dillon }
172577109eaSMatthew Dillon 
173577109eaSMatthew Dillon /*
174577109eaSMatthew Dillon  * md5_check:  check MD5 against file
175577109eaSMatthew Dillon  *
176577109eaSMatthew Dillon  *	Return -1 if check failed
177577109eaSMatthew Dillon  *	Return 0  if check succeeded
178577109eaSMatthew Dillon  *
179577109eaSMatthew Dillon  * dpath can be NULL, in which case we are force-updating
180577109eaSMatthew Dillon  * the source MD5.
181577109eaSMatthew Dillon  */
182577109eaSMatthew Dillon int
md5_check(const char * spath,const char * dpath)183577109eaSMatthew Dillon md5_check(const char *spath, const char *dpath)
184577109eaSMatthew Dillon {
185577109eaSMatthew Dillon     const char *sfile;
186577109eaSMatthew Dillon     char *dcode;
187577109eaSMatthew Dillon     int sdirlen;
188577109eaSMatthew Dillon     int r;
189577109eaSMatthew Dillon     MD5Node *node;
190577109eaSMatthew Dillon 
191577109eaSMatthew Dillon     r = -1;
192577109eaSMatthew Dillon 
193577109eaSMatthew Dillon     if ((sfile = strrchr(spath, '/')) != NULL)
194577109eaSMatthew Dillon 	++sfile;
195577109eaSMatthew Dillon     else
196577109eaSMatthew Dillon 	sfile = spath;
197577109eaSMatthew Dillon     sdirlen = sfile - spath;
198577109eaSMatthew Dillon 
199577109eaSMatthew Dillon     md5_cache(spath, sdirlen);
200577109eaSMatthew Dillon 
201577109eaSMatthew Dillon     node = md5_lookup(sfile);
202577109eaSMatthew Dillon 
203577109eaSMatthew Dillon     /*
204577109eaSMatthew Dillon      * If dpath == NULL, we are force-updating the source .MD5* files
205577109eaSMatthew Dillon      */
206577109eaSMatthew Dillon 
207577109eaSMatthew Dillon     if (dpath == NULL) {
208d5fdcd00SMatthew Dillon 	char *scode = doMD5File(spath, NULL, 0);
209577109eaSMatthew Dillon 
210577109eaSMatthew Dillon 	r = 0;
211577109eaSMatthew Dillon 	if (node->md_Code == NULL) {
212577109eaSMatthew Dillon 	    r = -1;
213577109eaSMatthew Dillon 	    node->md_Code = scode;
214577109eaSMatthew Dillon 	    MD5SCacheDirty = 1;
215577109eaSMatthew Dillon 	} else if (strcmp(scode, node->md_Code) != 0) {
216577109eaSMatthew Dillon 	    r = -1;
217577109eaSMatthew Dillon 	    free(node->md_Code);
218577109eaSMatthew Dillon 	    node->md_Code = scode;
219577109eaSMatthew Dillon 	    MD5SCacheDirty = 1;
220577109eaSMatthew Dillon 	} else {
221577109eaSMatthew Dillon 	    free(scode);
222577109eaSMatthew Dillon 	}
223577109eaSMatthew Dillon 	return(r);
224577109eaSMatthew Dillon     }
225577109eaSMatthew Dillon 
226577109eaSMatthew Dillon     /*
227577109eaSMatthew Dillon      * Otherwise the .MD5* file is used as a cache.
228577109eaSMatthew Dillon      */
229577109eaSMatthew Dillon 
230577109eaSMatthew Dillon     if (node->md_Code == NULL) {
231d5fdcd00SMatthew Dillon 	node->md_Code = doMD5File(spath, NULL, 0);
232577109eaSMatthew Dillon 	MD5SCacheDirty = 1;
233577109eaSMatthew Dillon     }
234577109eaSMatthew Dillon 
235d5fdcd00SMatthew Dillon     dcode = doMD5File(dpath, NULL, 1);
236577109eaSMatthew Dillon     if (dcode) {
237577109eaSMatthew Dillon 	if (strcmp(node->md_Code, dcode) == 0) {
238577109eaSMatthew Dillon 	    r = 0;
239577109eaSMatthew Dillon 	} else {
240d5fdcd00SMatthew Dillon 	    char *scode = doMD5File(spath, NULL, 0);
241577109eaSMatthew Dillon 
242577109eaSMatthew Dillon 	    if (strcmp(node->md_Code, scode) == 0) {
243577109eaSMatthew Dillon 		    free(scode);
244577109eaSMatthew Dillon 	    } else {
245577109eaSMatthew Dillon 		    free(node->md_Code);
246577109eaSMatthew Dillon 		    node->md_Code = scode;
247577109eaSMatthew Dillon 		    MD5SCacheDirty = 1;
248577109eaSMatthew Dillon 		    if (strcmp(node->md_Code, dcode) == 0)
249577109eaSMatthew Dillon 			r = 0;
250577109eaSMatthew Dillon 	    }
251577109eaSMatthew Dillon 	}
252577109eaSMatthew Dillon 	free(dcode);
253577109eaSMatthew Dillon     }
254577109eaSMatthew Dillon     return(r);
255577109eaSMatthew Dillon }
256577109eaSMatthew Dillon 
257de78d61cSzrj static char *
md5_file(const char * filename,char * buf)258de78d61cSzrj md5_file(const char *filename, char *buf)
259de78d61cSzrj {
260de78d61cSzrj     unsigned char digest[MD5_DIGEST_LENGTH];
261de78d61cSzrj     static const char hex[]="0123456789abcdef";
262de78d61cSzrj     MD5_CTX ctx;
263de78d61cSzrj     unsigned char buffer[4096];
264de78d61cSzrj     struct stat st;
265de78d61cSzrj     off_t size;
266de78d61cSzrj     int fd, bytes, i;
267de78d61cSzrj 
268de78d61cSzrj     fd = open(filename, O_RDONLY);
269de78d61cSzrj     if (fd < 0)
270de78d61cSzrj 	return NULL;
271de78d61cSzrj     if (fstat(fd, &st) < 0) {
272de78d61cSzrj 	bytes = -1;
273de78d61cSzrj 	goto err;
274de78d61cSzrj     }
275de78d61cSzrj 
276de78d61cSzrj     MD5_Init(&ctx);
277de78d61cSzrj     size = st.st_size;
278de78d61cSzrj     bytes = 0;
279de78d61cSzrj     while (size > 0) {
280de78d61cSzrj 	if ((size_t)size > sizeof(buffer))
281de78d61cSzrj 	     bytes = read(fd, buffer, sizeof(buffer));
282de78d61cSzrj 	else
283de78d61cSzrj 	     bytes = read(fd, buffer, size);
284de78d61cSzrj 	if (bytes < 0)
285de78d61cSzrj 	     break;
286de78d61cSzrj 	MD5_Update(&ctx, buffer, bytes);
287de78d61cSzrj 	size -= bytes;
288de78d61cSzrj     }
289de78d61cSzrj 
290de78d61cSzrj err:
291de78d61cSzrj     close(fd);
292de78d61cSzrj     if (bytes < 0)
293de78d61cSzrj 	return NULL;
294de78d61cSzrj 
295de78d61cSzrj     if (!buf)
296de78d61cSzrj 	buf = malloc(MD5_DIGEST_LENGTH * 2 + 1);
297de78d61cSzrj     if (!buf)
298de78d61cSzrj 	return NULL;
299de78d61cSzrj 
300de78d61cSzrj     MD5_Final(digest, &ctx);
301de78d61cSzrj     for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
302de78d61cSzrj 	buf[2*i] = hex[digest[i] >> 4];
303de78d61cSzrj 	buf[2*i+1] = hex[digest[i] & 0x0f];
304de78d61cSzrj     }
305de78d61cSzrj     buf[MD5_DIGEST_LENGTH * 2] = '\0';
306de78d61cSzrj 
307de78d61cSzrj     return buf;
308de78d61cSzrj }
309de78d61cSzrj 
310577109eaSMatthew Dillon char *
doMD5File(const char * filename,char * buf,int is_target)311d5fdcd00SMatthew Dillon doMD5File(const char *filename, char *buf, int is_target)
312577109eaSMatthew Dillon {
313577109eaSMatthew Dillon     if (SummaryOpt) {
314577109eaSMatthew Dillon 	struct stat st;
315577109eaSMatthew Dillon 	if (stat(filename, &st) == 0) {
316c0538630SMatthew Dillon 	    uint64_t size = st.st_size;
317d5fdcd00SMatthew Dillon 	    if (is_target)
318d5fdcd00SMatthew Dillon 		    CountTargetReadBytes += size;
319d5fdcd00SMatthew Dillon 	    else
320d5fdcd00SMatthew Dillon 		    CountSourceReadBytes += size;
321577109eaSMatthew Dillon 	}
322577109eaSMatthew Dillon     }
323*3132c289SAaron LI 
324de78d61cSzrj     return md5_file(filename, buf);
325577109eaSMatthew Dillon }
326577109eaSMatthew Dillon 
327