Merge from vendor branch TNFTP:
[dragonfly.git] / bin / cpdup / md5.c
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.1 2006/04/25 21:30:45 dillon 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);
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 %d %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             CountReadBytes += 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);
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);
232         MD5SCacheDirty = 1;
233     }
234
235     dcode = doMD5File(dpath, NULL);
236     if (dcode) {
237         if (strcmp(node->md_Code, dcode) == 0) {
238             r = 0;
239         } else {
240             char *scode = doMD5File(spath, NULL);
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)
259 {
260     if (SummaryOpt) {
261         struct stat st;
262         if (stat(filename, &st) == 0) {
263             u_int64_t size = st.st_blocks * 512;
264             if (st.st_size % 512) 
265                 size += st.st_size % 512 - 512;
266             CountReadBytes += size;
267         }
268     }
269     return MD5File(filename, buf);
270 }
271