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