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.2 2008/03/22 18:09:16 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, 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 %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 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 u_int64_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