xref: /dragonfly/bin/cpdup/md5.c (revision 36a3d1d6)
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.
7  *
8  * $DragonFly: src/bin/cpdup/md5.c,v 1.3 2008/11/10 14:30:02 swildner Exp $
9  */
10 
11 #include "cpdup.h"
12 
13 typedef struct MD5Node {
14     struct MD5Node *md_Next;
15     char *md_Name;
16     char *md_Code;
17     int md_Accessed;
18 } MD5Node;
19 
20 static MD5Node *md5_lookup(const char *sfile);
21 static void md5_cache(const char *spath, int sdirlen);
22 static char *doMD5File(const char *filename, char *buf, int is_target);
23 
24 static char *MD5SCache;		/* cache source directory name */
25 static MD5Node *MD5Base;
26 static int MD5SCacheDirLen;
27 static int MD5SCacheDirty;
28 
29 void
30 md5_flush(void)
31 {
32     if (MD5SCacheDirty && MD5SCache) {
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) {
40 		    fprintf(fo, "%s %zu %s\n",
41 			node->md_Code,
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 
70 static void
71 md5_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 	    /*
126 	     * extracting md_Name - name may contain embedded control
127 	     * characters.
128 	     */
129 	    CountSourceReadBytes += nlen+1;
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 
148 static MD5Node *
149 md5_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  */
182 int
183 md5_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) {
208 	char *scode = doMD5File(spath, NULL, 0);
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) {
231 	node->md_Code = doMD5File(spath, NULL, 0);
232 	MD5SCacheDirty = 1;
233     }
234 
235     dcode = doMD5File(dpath, NULL, 1);
236     if (dcode) {
237 	if (strcmp(node->md_Code, dcode) == 0) {
238 	    r = 0;
239 	} else {
240 	    char *scode = doMD5File(spath, NULL, 0);
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 
257 char *
258 doMD5File(const char *filename, char *buf, int is_target)
259 {
260     if (SummaryOpt) {
261 	struct stat st;
262 	if (stat(filename, &st) == 0) {
263 	    uint64_t size = st.st_size;
264 	    if (is_target)
265 		    CountTargetReadBytes += size;
266 	    else
267 		    CountSourceReadBytes += size;
268 	}
269     }
270     return MD5File(filename, buf);
271 }
272 
273