1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <time.h>
27 #include <utime.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include "proto.h"
34 
35 extern long gmtoffset;
36 extern GLOBAL *global;
37 
38 /* hold lock when:
39 	calling:
40 		cache_hash_add
41 		cache_hash_remove
42 		cache_clean
43 		cache_filename
44 		cache_disk_delete
45 		cache_mem_delete
46 		cache_journal_*
47 		*_unlocked
48 		select_store
49 
50 	when accessing any members of a cachemap struct besides key, data, offset, and size.
51 
52 	no other threads may change any cachemap members when it's open for writing.
53 
54 	a thread with a cachemap open for writing may not change any member besides data without
55 	the mutex locked.
56 
57 	cache->stores is protected with cache->lock
58 */
59 
cache_load(struct cache_store * s,XML_LIST * xml_list)60 struct cache_store *cache_load(struct cache_store *s, XML_LIST * xml_list)
61 {
62 	struct cache_store *head = s, *stores = s;
63 
64 	while ((xml_list = xml_section(xml_list, "<cache>"))) {
65 		XML_LIST_LOOP(xml_list, "<cache>") {
66 			XML_LIST_CMP(xml_list, "<enabled>") {
67 				xml_list = xml_list->next;
68 				if (xml_list->type == XML_VALUE) {
69 					if (!strcasecmp(xml_list->item, "false"))
70 						global->cache->enabled = FALSE;
71 					else
72 						global->cache->enabled = TRUE;
73 				}
74 			}
75 			XML_LIST_CMP(xml_list, "<violaterfc>") {
76 				xml_list = xml_list->next;
77 				if (xml_list->type == XML_VALUE) {
78 					if (!strcasecmp(xml_list->item, "false"))
79 						global->cache->violaterfc = FALSE;
80 					else
81 						global->cache->violaterfc = TRUE;
82 				}
83 			}
84 			XML_LIST_CMP(xml_list, "<maxmemsize>") {
85 				xml_list = xml_list->next;
86 				if (xml_list->type == XML_VALUE)
87 					global_cache_insert(xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
88 			}
89 			XML_LIST_CMP(xml_list, "<memextra>") {
90 				xml_list = xml_list->next;
91 				if (xml_list->type == XML_VALUE)
92 					global_cache_insert(NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
93 			}
94 			XML_LIST_CMP(xml_list, "<maxage>") {
95 				xml_list = xml_list->next;
96 				if (xml_list->type == XML_VALUE)
97 					global_cache_insert(NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
98 			}
99 			XML_LIST_CMP(xml_list, "<validate>") {
100 				xml_list = xml_list->next;
101 				if (xml_list->type == XML_VALUE)
102 					global_cache_insert(NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
103 			}
104 			XML_LIST_CMP(xml_list, "<lmfactor>") {
105 				xml_list = xml_list->next;
106 				if (xml_list->type == XML_VALUE)
107 					global_cache_insert(NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
108 			}
109 			XML_LIST_CMP(xml_list, "<maxsize>") {
110 				xml_list = xml_list->next;
111 				if (xml_list->type == XML_VALUE)
112 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
113 			}
114 			XML_LIST_CMP(xml_list, "<minage>") {
115 				xml_list = xml_list->next;
116 				if (xml_list->type == XML_VALUE)
117 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL, NULL);
118 			}
119 			XML_LIST_CMP(xml_list, "<minsize>") {
120 				xml_list = xml_list->next;
121 				if (xml_list->type == XML_VALUE)
122 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL, NULL);
123 			}
124 			XML_LIST_CMP(xml_list, "<maxwaitsize>") {
125 				xml_list = xml_list->next;
126 				if (xml_list->type == XML_VALUE)
127 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL, NULL);
128 			}
129 			XML_LIST_CMP(xml_list, "<prefetchwindow>") {
130 				xml_list = xml_list->next;
131 				if (xml_list->type == XML_VALUE)
132 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL, NULL);
133 			}
134 			XML_LIST_CMP(xml_list, "<icpport>") {
135 				xml_list = xml_list->next;
136 				if (xml_list->type == XML_VALUE)
137 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL, NULL);
138 			}
139 			XML_LIST_CMP(xml_list, "<icptimeout>") {
140 				xml_list = xml_list->next;
141 				if (xml_list->type == XML_VALUE)
142 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item, NULL);
143 
144 			}
145 			XML_LIST_CMP(xml_list, "<sbalancemethod>") {
146 				xml_list = xml_list->next;
147 				if (xml_list->type == XML_VALUE)
148 					global_cache_insert(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, xml_list->item);
149 
150 			}
151 
152 			XML_LIST_CMP(xml_list, "<store>") {
153 				stores = stores_list_new(stores);
154 				if (head == NULL)
155 					head = stores;
156 
157 				stores->id = global->cache->id++;
158 
159 				XML_LIST_LOOP(xml_list, "<store>") {
160 					XML_LIST_CMP(xml_list, "<enabled>") {
161 						xml_list = xml_list->next;
162 						if (xml_list->type == XML_VALUE) {
163 							if (!strcasecmp(xml_list->item, "false"))
164 								stores->enabled = FALSE;
165 							else
166 								stores->enabled = TRUE;
167 						}
168 					}
169 					XML_LIST_CMP(xml_list, "<comment>") {
170 						xml_list = xml_list->next;
171 						if (xml_list->type == XML_VALUE)
172 							stores_list_insert(stores, xml_list->item, NULL, NULL, NULL, NULL);
173 					}
174 					XML_LIST_CMP(xml_list, "<profiles>") {
175 						xml_list = xml_list->next;
176 						if (xml_list->type == XML_VALUE)
177 							stores_list_insert(stores, NULL, xml_list->item, NULL, NULL, NULL);
178 					}
179 					XML_LIST_CMP(xml_list, "<path>") {
180 						xml_list = xml_list->next;
181 						if (xml_list->type == XML_VALUE)
182 							stores_list_insert(stores, NULL, NULL, xml_list->item, NULL, NULL);
183 					}
184 					XML_LIST_CMP(xml_list, "<maxdisksize>") {
185 						xml_list = xml_list->next;
186 						if (xml_list->type == XML_VALUE)
187 							stores_list_insert(stores, NULL, NULL, NULL, xml_list->item, NULL);
188 					}
189 					XML_LIST_CMP(xml_list, "<diskextra>") {
190 						xml_list = xml_list->next;
191 						if (xml_list->type == XML_VALUE)
192 							stores_list_insert(stores, NULL, NULL, NULL, NULL, xml_list->item);
193 					}
194 				}
195 			}
196 		}
197 	}
198 
199 	return head;
200 }
201 
cache_xml(struct cache_store * stores,XML_LIST * xml_list)202 XML_LIST *cache_xml(struct cache_store * stores, XML_LIST * xml_list)
203 {
204 	char buf[16], *ptr;
205 	struct cache_store *sl;
206 
207 	pthread_mutex_lock(&global->cache->lock);
208 
209 	xml_list = xml_list_add(xml_list, "<cache>", XML_TAG);
210 
211 	xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
212 	xml_list = xml_list_add(xml_list, (global->cache->enabled == TRUE) ? "true" : "false", XML_VALUE);
213 	xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
214 
215 	xml_list = xml_list_add(xml_list, "<violaterfc>", XML_TAG);
216 	xml_list = xml_list_add(xml_list, (global->cache->violaterfc == FALSE) ? "false" : "true", XML_VALUE);
217 	xml_list = xml_list_add(xml_list, "</violaterfc>", XML_TAG);
218 
219 	xml_list = xml_list_add(xml_list, "<maxmemsize>", XML_TAG);
220 	snprintf(buf, sizeof(buf), "%u", global->cache->maxmemsize);
221 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
222 	xml_list = xml_list_add(xml_list, "</maxmemsize>", XML_TAG);
223 
224 	xml_list = xml_list_add(xml_list, "<memextra>", XML_TAG);
225 	snprintf(buf, sizeof(buf), "%u", global->cache->memextra);
226 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
227 	xml_list = xml_list_add(xml_list, "</memextra>", XML_TAG);
228 
229 	xml_list = xml_list_add(xml_list, "<maxage>", XML_TAG);
230 	snprintf(buf, sizeof(buf), "%d", global->cache->maxage);
231 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
232 	xml_list = xml_list_add(xml_list, "</maxage>", XML_TAG);
233 
234 	xml_list = xml_list_add(xml_list, "<validate>", XML_TAG);
235 	snprintf(buf, sizeof(buf), "%d", global->cache->validate);
236 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
237 	xml_list = xml_list_add(xml_list, "</validate>", XML_TAG);
238 
239 	xml_list = xml_list_add(xml_list, "<lmfactor>", XML_TAG);
240 	snprintf(buf, sizeof(buf), "%d", global->cache->lmfactor);
241 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
242 	xml_list = xml_list_add(xml_list, "</lmfactor>", XML_TAG);
243 
244 	xml_list = xml_list_add(xml_list, "<maxsize>", XML_TAG);
245 	snprintf(buf, sizeof(buf), "%u", global->cache->maxsize);
246 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
247 	xml_list = xml_list_add(xml_list, "</maxsize>", XML_TAG);
248 
249 	xml_list = xml_list_add(xml_list, "<minage>", XML_TAG);
250 	snprintf(buf, sizeof(buf), "%d", global->cache->minage);
251 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
252 	xml_list = xml_list_add(xml_list, "</minage>", XML_TAG);
253 
254 	xml_list = xml_list_add(xml_list, "<minsize>", XML_TAG);
255 	snprintf(buf, sizeof(buf), "%u", global->cache->minsize);
256 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
257 	xml_list = xml_list_add(xml_list, "</minsize>", XML_TAG);
258 
259 	xml_list = xml_list_add(xml_list, "<maxwaitsize>", XML_TAG);
260 	snprintf(buf, sizeof(buf), "%u", global->cache->maxwaitsize);
261 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
262 	xml_list = xml_list_add(xml_list, "</maxwaitsize>", XML_TAG);
263 
264 	xml_list = xml_list_add(xml_list, "<prefetchwindow>", XML_TAG);
265 	snprintf(buf, sizeof(buf), "%d", global->cache->prefetchwindow);
266 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
267 	xml_list = xml_list_add(xml_list, "</prefetchwindow>", XML_TAG);
268 
269 	xml_list = xml_list_add(xml_list, "<icpport>", XML_TAG);
270 	snprintf(buf, sizeof(buf), "%d", global->cache->icpport);
271 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
272 	xml_list = xml_list_add(xml_list, "</icpport>", XML_TAG);
273 
274 	xml_list = xml_list_add(xml_list, "<icptimeout>", XML_TAG);
275 	snprintf(buf, sizeof(buf), "%d", global->cache->icptimeout);
276 	xml_list = xml_list_add(xml_list, buf, XML_VALUE);
277 	xml_list = xml_list_add(xml_list, "</icptimeout>", XML_TAG);
278 
279 	xml_list = xml_list_add(xml_list, "<sbalancemethod>", XML_TAG);
280 	xml_list = xml_list_add(xml_list, (global->cache->sbalancemethod == SBALANCE_FILLSIZE) ? "fillsize" : "fillpercent", XML_VALUE);
281 	xml_list = xml_list_add(xml_list, "</sbalancemethod>", XML_TAG);
282 
283 	for (sl = global->cache->stores; sl; sl = sl->next) {
284 		xml_list = xml_list_add(xml_list, "<store>", XML_TAG);
285 
286 		xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
287 		xml_list = xml_list_add(xml_list, (sl->enabled == TRUE) ? "true" : "false", XML_VALUE);
288 		xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
289 
290 		if (sl->comment != NULL) {
291 			xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
292 			ptr = string_to_xml(sl->comment);
293 			xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
294 			xfree(ptr);
295 			xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
296 		}
297 
298 		if (sl->profiles != NULL) {
299 			xml_list = xml_list_add(xml_list, "<profiles>", XML_TAG);
300 			ptr = array_merge(sl->profiles, ',');
301 			xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
302 			xfree(ptr);
303 			xml_list = xml_list_add(xml_list, "</profiles>", XML_TAG);
304 		}
305 
306 		if (sl->path != NULL) {
307 			xml_list = xml_list_add(xml_list, "<path>", XML_TAG);
308 			ptr = string_to_xml(sl->path);
309 			xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
310 			xfree(ptr);
311 			xml_list = xml_list_add(xml_list, "</path>", XML_TAG);
312 		}
313 
314 		xml_list = xml_list_add(xml_list, "<maxdisksize>", XML_TAG);
315 		snprintf(buf, sizeof(buf), "%u", sl->maxdisksize);
316 		xml_list = xml_list_add(xml_list, buf, XML_VALUE);
317 		xml_list = xml_list_add(xml_list, "</maxdisksize>", XML_TAG);
318 
319 		xml_list = xml_list_add(xml_list, "<diskextra>", XML_TAG);
320 		snprintf(buf, sizeof(buf), "%u", sl->diskextra);
321 		xml_list = xml_list_add(xml_list, buf, XML_VALUE);
322 		xml_list = xml_list_add(xml_list, "</diskextra>", XML_TAG);
323 
324 		xml_list = xml_list_add(xml_list, "</store>", XML_TAG);
325 	}
326 
327 	xml_list = xml_list_add(xml_list, "</cache>", XML_TAG);
328 
329 	pthread_mutex_unlock(&global->cache->lock);
330 
331 	return xml_list;
332 }
333 
global_cache_insert(char * maxmemsize,char * memextra,char * maxage,char * validate,char * lmfactor,char * maxsize,char * minage,char * minsize,char * maxwaitsize,char * prefetchwindow,char * icpport,char * icptimeout,char * sbalancemethod)334 void global_cache_insert(char *maxmemsize, char *memextra, char *maxage, char *validate, char *lmfactor, char *maxsize, char *minage, char *minsize, char *maxwaitsize, char *prefetchwindow, char *icpport, char *icptimeout, char *sbalancemethod)
335 {
336 	if (maxmemsize != NULL) {
337 		global->cache->maxmemsize = 0;
338 		if (strcmp(maxmemsize, ""))
339 			global->cache->maxmemsize = string_to_size(maxmemsize);
340 	}
341 
342 	if (memextra != NULL) {
343 		global->cache->memextra = 0;
344 		if (strcmp(memextra, ""))
345 			global->cache->memextra = string_to_size(memextra);
346 	}
347 
348 	if (maxage != NULL) {
349 		global->cache->maxage = 0;
350 
351 		if (strcmp(maxage, ""))
352 			global->cache->maxage = atoi(maxage);
353 	}
354 
355 	if (validate != NULL) {
356 		global->cache->validate = 0;
357 
358 		if (strcmp(validate, ""))
359 			global->cache->validate = atoi(validate);
360 	}
361 
362 	if (lmfactor != NULL) {
363 		global->cache->lmfactor = 0;
364 
365 		if (strcmp(lmfactor, ""))
366 			global->cache->lmfactor = atoi(lmfactor);
367 	}
368 
369 	if (maxsize != NULL) {
370 		global->cache->maxsize = 0;
371 
372 		if (strcmp(maxsize, ""))
373 			global->cache->maxsize = string_to_size(maxsize);
374 	}
375 
376 	if (minage != NULL) {
377 		global->cache->minage = 0;
378 
379 		if (strcmp(minage, ""))
380 			global->cache->minage = atoi(minage);
381 	}
382 
383 	if (minsize != NULL) {
384 		global->cache->minsize = 0;
385 
386 		if (strcmp(minsize, ""))
387 			global->cache->minsize = string_to_size(minsize);
388 	}
389 
390 	if (maxwaitsize != NULL) {
391 		global->cache->maxwaitsize = 0;
392 
393 		if (strcmp(maxwaitsize, ""))
394 			global->cache->maxwaitsize = string_to_size(maxwaitsize);
395 	}
396 
397 	if (prefetchwindow != NULL) {
398 		global->cache->prefetchwindow = 10;
399 
400 		if (strcmp(prefetchwindow, ""))
401 			global->cache->prefetchwindow = atoi(prefetchwindow);
402 	}
403 
404 	if (icpport != NULL) {
405 		global->cache->icpport = 0;
406 
407 		if (strcmp(icpport, ""))
408 			global->cache->icpport = atoi(icpport);
409 	}
410 
411 	if (icptimeout != NULL) {
412 		global->cache->icptimeout = ICP_TIMEOUT;
413 
414 		if (strcmp(icptimeout, ""))
415 			global->cache->icptimeout = atoi(icptimeout);
416 	}
417 
418 	if (sbalancemethod != NULL) {
419 		global->cache->sbalancemethod = SBALANCE_FILLSIZE;
420 
421 		if (!strcasecmp(sbalancemethod, "fillsize"))
422 			global->cache->sbalancemethod = SBALANCE_FILLSIZE;
423 		else if (!strcasecmp(sbalancemethod, "fillpercent"))
424 			global->cache->sbalancemethod = SBALANCE_FILLPERCENT;
425 	}
426 }
427 
stores_list_new(struct cache_store * x)428 struct cache_store *stores_list_new(struct cache_store *x)
429 {
430 
431 	if (x == NULL) {
432 		x = xmalloc(sizeof(struct cache_store));
433 		x->prev = NULL;
434 	} else {
435 		while (x->next != NULL)
436 			x = x->next;
437 		x->next = xmalloc(sizeof(struct cache_store));
438 		x->next->prev = x;
439 		x = x->next;
440 	}
441 
442 	x->enabled = TRUE;
443 	x->next = NULL;
444 	x->comment = NULL;
445 	x->profiles = NULL;
446 	x->path = NULL;
447 	x->journalentries = 0;
448 	x->journalfd = -1;
449 	x->diskentries = 0;
450 	x->disksize = x->maxdisksize = x->diskextra = 0;
451 
452 
453 	return x;
454 }
455 
stores_list_delete(struct cache_store * x)456 struct cache_store *stores_list_delete(struct cache_store *x)
457 {
458 	struct cache_store *start = x;
459 
460 	while (start->prev != NULL)
461 		start = start->prev;
462 
463 	if (x->next != NULL)
464 		x->next->prev = x->prev;
465 	if (x->prev != NULL)
466 		x->prev->next = x->next;
467 	else
468 		start = x->next;
469 
470 	if (x->profiles != NULL)
471 		array_free(x->profiles);
472 	FREE_AND_NULL(x->comment);
473 	FREE_AND_NULL(x->path);
474 
475 	xfree(x);
476 
477 	return start;
478 }
479 
stores_list_free(struct cache_store * x)480 void stores_list_free(struct cache_store *x)
481 {
482 	struct cache_store *tmp;
483 
484 	while (x != NULL) {
485 		tmp = x->next;
486 
487 		if (x->profiles != NULL)
488 			array_free(x->profiles);
489 		FREE_AND_NULL(x->comment);
490 		FREE_AND_NULL(x->path);
491 
492 		x = tmp;
493 	}
494 }
495 
stores_list_insert(struct cache_store * x,char * comment,char * profiles,char * path,char * maxdisksize,char * diskextra)496 void stores_list_insert(struct cache_store *x, char *comment, char *profiles, char *path, char *maxdisksize, char *diskextra)
497 {
498 	if (comment != NULL) {
499 		FREE_AND_NULL(x->comment);
500 
501 		if (strcmp(comment, ""))
502 			x->comment = xstrdup(comment);
503 	}
504 
505 	if (profiles != NULL) {
506 		if (x->profiles != NULL)
507 			array_free(x->profiles);
508 
509 		if (strcmp(profiles, ""))
510 			x->profiles = string_break(profiles, ',');
511 		else
512 			x->profiles = NULL;
513 	}
514 
515 	if (path != NULL) {
516 		FREE_AND_NULL(x->path);
517 
518 		if (strcmp(path, ""))
519 			x->path = xstrdup(path);
520 	}
521 
522 	if (maxdisksize != NULL) {
523 		x->maxdisksize = 0;
524 
525 		if (strcmp(maxdisksize, ""))
526 			x->maxdisksize = string_to_size(maxdisksize);
527 	}
528 
529 	if (diskextra != NULL) {
530 		x->diskextra = 0;
531 
532 		if (strcmp(diskextra, ""))
533 			x->diskextra = string_to_size(diskextra);
534 	}
535 }
536 
select_store(CONNECTION * connection,CACHE * cache,CACHEMAP * cachemap)537 void select_store(CONNECTION * connection, CACHE * cache, CACHEMAP * cachemap)
538 {
539 	int ptmp;
540 	size_t lowest = ~0;
541 	struct cache_store *selected = NULL, *stores;
542 
543 	ASSERT(connection != NULL);
544 
545 	for (stores = cache->stores; stores; stores = stores->next) {
546 		if (stores->enabled == FALSE)
547 			continue;
548 
549 		if (!profile_find(connection->profiles, stores->profiles))
550 			continue;
551 
552 		/* check if the entry is properly configured, so assumptions can be made later */
553 		if (stores->path == NULL || stores->maxdisksize == 0)
554 			continue;
555 
556 		switch (cache->sbalancemethod) {
557 		case SBALANCE_FILLSIZE:
558 			if (stores->disksize < lowest) {
559 				lowest = stores->disksize;
560 				selected = stores;
561 			}
562 			break;
563 		case SBALANCE_FILLPERCENT:
564 			ptmp = (int) (((float) stores->disksize / (float) stores->maxdisksize) * 100.0);
565 			if (ptmp < lowest) {
566 				lowest = ptmp;
567 				selected = stores;
568 			}
569 			break;
570 		}
571 
572 	}
573 
574 	cachemap->store = selected;
575 }
576 
stores_update(CACHE * cache)577 void stores_update(CACHE * cache)
578 {
579 	int i;
580 	struct cache_store *stores;
581 	CACHEMAP *cachemap;
582 	struct CACHEMAPLIST_T *cl, *tmp;
583 
584 	/* this routine is called whenever the cache disk store configuration is changed, it will resynchronize everything
585 	   in the cache hash table. */
586 	for (i = 0; i < cache->hashsize; i++) {
587 		for (cl = cache->cachemap[i]; cl; cl = tmp) {
588 			tmp = cl->next;
589 			cachemap = cl->cachemap;
590 
591 			if (cachemap->store != NULL) {
592 				for (stores = cache->stores; stores && cachemap->store != stores; stores = stores->next);
593 				if (stores == NULL || stores->path == NULL) {
594 					/* this cachemap's filesystem store information doesn't exist anymore */
595 
596 					if (!(cachemap->flags & CACHE_MEM)) {
597 						cache_hash_remove(cache, cachemap);
598 						cache_free(cache, cachemap);
599 					} else {
600 						/* it's mapped in memory, so it may still be useful. */
601 						cl->cachemap->store = NULL;
602 						cl->cachemap->flags &= ~CACHE_DISK;
603 					}
604 				}
605 			}
606 		}
607 	}
608 
609 	for (stores = global->cache->stores; stores; stores = stores->next) {
610 		if (stores->journalfd != -1) {
611 			close(stores->journalfd);
612 			stores->journalfd = -1;
613 		}
614 
615 		if (stores->path == NULL)
616 			continue;
617 
618 		stores->diskentries = stores->disksize = 0;
619 
620 		/* re-reading the journals will reassociate each cache object in the hash table with a cache disk store. */
621 		cache_journal_read(global->cache, stores);
622 		cache_journal_write(global->cache, stores);
623 	}
624 }
625 
cache_init()626 CACHE *cache_init()
627 {
628 	CACHE *cache;
629 
630 	cache = xmalloc(sizeof(CACHE));
631 
632 	pthread_mutex_init(&cache->lock, NULL);
633 
634 	cache->enabled = FALSE;
635 	cache->id = 0;
636 	cache->sbalancemethod = SBALANCE_FILLSIZE;
637 	cache->violaterfc = FALSE;
638 	cache->hashsize = CACHEMAP_HASH_SIZE;
639 	cache->memsize = cache->maxmemsize = cache->memextra = 0;
640 	cache->minage = cache->maxage = 0;
641 	cache->lmfactor = 0;
642 	cache->validate = 0;
643 	cache->minsize = cache->maxsize = 0;
644 	cache->mementries = 0;
645 	cache->hit = cache->miss = 0;
646 	cache->maxwaitsize = 0;
647 	cache->prefetchwindow = 10;
648 	cache->lastclean = 0;
649 	cache->icpport = 0;
650 	cache->icptimeout = ICP_TIMEOUT;
651 	cache->stores = NULL;
652 
653 	cache->cachemap = xmalloc(sizeof(struct CACHEMAPLIST_T *) * cache->hashsize);
654 	memset(cache->cachemap, 0, sizeof(struct CACHEMAPLIST_T *) * cache->hashsize);
655 
656 	return cache;
657 }
658 
cache_hash_find(CACHE * cache,char * key)659 CACHEMAP *cache_hash_find(CACHE * cache, char *key)
660 {
661 	unsigned int k;
662 	struct CACHEMAPLIST_T *cachemaplist;
663 	CACHEMAP *cachemap = NULL;
664 
665 	k = hash_key(cache->hashsize, key);
666 
667 	cachemaplist = cache->cachemap[k];
668 
669 	while (cachemaplist && strcmp(cachemaplist->cachemap->key, key))
670 		cachemaplist = cachemaplist->next;
671 
672 	if (cachemaplist != NULL)
673 		cachemap = cachemaplist->cachemap;
674 
675 	return cachemap;
676 }
677 
cache_filename(CACHE * cache,CACHEMAP * cachemap,char * buf,int len)678 int cache_filename(CACHE * cache, CACHEMAP * cachemap, char *buf, int len)
679 {
680 	int x;
681 	unsigned char k;
682 	char *key = cachemap->key;
683 
684 	k = hash_key(256, cachemap->key);
685 	x = snprintf(buf, len, "%s/%.2x/", cachemap->store->path, k);
686 	for (; x < len - 1 && *key; x++, key++)
687 		buf[x] = (*key == '/') ? '\\' : *key;
688 	buf[x] = '\0';
689 
690 	return k;
691 }
692 
cache_hash_add(CACHE * cache,CACHEMAP * cachemap)693 void cache_hash_add(CACHE * cache, CACHEMAP * cachemap)
694 {
695 	unsigned int k;
696 	struct CACHEMAPLIST_T *cachemaplist;
697 
698 	k = hash_key(cache->hashsize, cachemap->key);
699 
700 	if (cache->cachemap[k] == NULL) {
701 		cache->cachemap[k] = xmalloc(sizeof(struct CACHEMAPLIST_T));
702 		cache->cachemap[k]->cachemap = cachemap;
703 		cache->cachemap[k]->next = cache->cachemap[k]->prev = NULL;
704 	} else {
705 		for (cachemaplist = cache->cachemap[k]; strcmp(cachemaplist->cachemap->key, cachemap->key) && cachemaplist->next; cachemaplist = cachemaplist->next);
706 		ASSERT(cachemaplist->next == NULL);
707 
708 		cachemaplist->next = xmalloc(sizeof(struct CACHEMAPLIST_T));
709 		cachemaplist->next->cachemap = cachemap;
710 		cachemaplist->next->prev = cachemaplist;
711 		cachemaplist->next->next = NULL;
712 	}
713 }
714 
cache_hash_remove(CACHE * cache,CACHEMAP * cachemap)715 void cache_hash_remove(CACHE * cache, CACHEMAP * cachemap)
716 {
717 	unsigned int k;
718 	struct CACHEMAPLIST_T *cachemaplist;
719 
720 	k = hash_key(cache->hashsize, cachemap->key);
721 
722 	for (cachemaplist = cache->cachemap[k]; cachemaplist && strcmp(cachemaplist->cachemap->key, cachemap->key); cachemaplist = cachemaplist->next);
723 	if (cachemaplist != NULL) {
724 		if (cachemaplist->next != NULL)
725 			cachemaplist->next->prev = cachemaplist->prev;
726 		if (cachemaplist->prev != NULL)
727 			cachemaplist->prev->next = cachemaplist->next;
728 		else
729 			cache->cachemap[k] = cachemaplist->next;
730 
731 		xfree(cachemaplist);
732 	} else
733 		ASSERT(0);
734 }
735 
736 
737 
cache_open(CONNECTION * connection,CACHE * cache,char * key,char * header,int flags)738 CACHEMAP *cache_open(CONNECTION * connection, CACHE * cache, char *key, char *header, int flags)
739 {
740 	int fd;
741 	time_t utctime;
742 	char buf[1024], *ptr;
743 	CACHEMAP *cachemap;
744 	struct stat st;
745 
746 	pthread_mutex_lock(&cache->lock);
747 
748 	if (cache->enabled == FALSE) {
749 		pthread_mutex_unlock(&cache->lock);
750 
751 		return NULL;
752 	}
753 
754 	utctime = utc_time(NULL);
755 
756 	cachemap = cache_hash_find(cache, key);
757 
758 	if (cachemap != NULL) {
759 		if ((cachemap->flags & CACHE_PREFETCHED) && utctime - cachemap->mtime > cache->prefetchwindow)
760 			cachemap->flags &= ~CACHE_PREFETCHED;
761 
762 		if (flags & CACHE_WRITING) {
763 			/* this will solve a race condition where a URL gets added to the prefetch
764 			   queue more than once and several threads try to prefetch it at
765 			   about the same time. */
766 			if (cachemap->refcount != 0 || (cachemap->flags & CACHE_PREFETCHED)) {
767 				if (cachemap->flags & CACHE_PREFETCHED)
768 					putlog(MMLOG_DEBUG, "attempted to recreate cached file within prefetch window");
769 
770 				cachemap = NULL;
771 
772 				goto out;
773 			}
774 
775 			putlog(MMLOG_DEBUG, "recreating cache file");
776 
777 			/* recreate existing cache file */
778 			cache_invalidate_unlocked(cache, cachemap);
779 
780 			cachemap = cachemap_new(cache);
781 			cachemap->key = xstrdup(key);
782 			cachemap->size = cachemap->realsize = cachemap->offset = 0;
783 			cachemap->mtime = utctime;
784 			cachemap->flags = flags;
785 			cachemap->refcount = 1;
786 
787 			cache_hash_add(cache, cachemap);
788 		} else {
789 			if (cachemap->flags & CACHE_INVALID) {
790 				if (cachemap->flags & CACHE_INVALID)
791 					putlog(MMLOG_DEBUG, "cache object is invalid");
792 
793 				cachemap = NULL;
794 
795 				goto out;
796 			}
797 
798 			if (cachemap->flags & CACHE_WRITING) {
799 				/* wait for thread writing the cache file to complete.
800 				   this isn't a very good way to do things, I would rather
801 				   be able to read from the cache as it is being written;
802 				   but this becomes very complex with threading, and I don't
803 				   think it's worth the trouble to handle rare cases like this */
804 
805 				if ((cachemap->header->content_length != -1 && cachemap->header->content_length > cache->maxwaitsize) || cachemap->size > cache->maxwaitsize) {
806 					/* file is too large to wait on */
807 					putlog(MMLOG_DEBUG, "cache file too large to wait on");
808 
809 					cachemap = NULL;
810 
811 					goto out;
812 				}
813 
814 				/* increase refcount incase thread downloading file
815 				   tries to invalidate it */
816 				cachemap->refcount++;
817 
818 				pthread_cond_wait(&cachemap->writecompletecond, &cache->lock);
819 
820 				if (cachemap->flags & CACHE_INVALID) {
821 					cache_invalidate_unlocked(cache, cachemap);
822 
823 					cachemap = NULL;
824 
825 					goto out;
826 				}
827 
828 				cachemap->refcount--;
829 
830 				/* did we stop waiting because it was too large ? */
831 				if (cachemap->size > cache->maxwaitsize) {
832 					putlog(MMLOG_DEBUG, "stopped waiting on large cache file");
833 
834 					cachemap = NULL;
835 
836 					goto out;
837 				}
838 
839 				putlog(MMLOG_DEBUG, "waited for cache file that was being written");
840 			}
841 
842 			if (!(cachemap->flags & CACHE_MEM) && cachemap->store != NULL) {
843 				ASSERT(cachemap->flags & CACHE_DISK);
844 				ASSERT(cachemap->data == NULL);
845 				ASSERT(cachemap->fd == -1);
846 				ASSERT(cachemap->refcount == 0);
847 
848 				cache_filename(cache, cachemap, buf, sizeof(buf));
849 
850 				fd = open(buf, O_RDONLY);
851 				if (fd != -1) {
852 					fstat(fd, &st);
853 					ptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
854 
855 					if (ptr != MAP_FAILED) {
856 						cachemap->mtime = st.st_mtime + gmtoffset;
857 						cachemap->atime = st.st_atime + gmtoffset;
858 
859 						if (cachemap->size != st.st_size) {
860 							putlog(MMLOG_CACHE, "file meta information didn't match journaled data");
861 
862 							/* compensate for unexpected change in file size */
863 							cachemap->store->disksize += (int) (st.st_size - cachemap->size);
864 							cachemap->size = cachemap->realsize = st.st_size;
865 
866 							/* add journal entry with updated file information */
867 							cache_journal_add(cache, cachemap);
868 						} else
869 							cachemap->size = cachemap->realsize = st.st_size;
870 
871 						cachemap->data = ptr;
872 						cachemap->flags |= CACHE_MEM;
873 
874 						cache->memsize += cachemap->size;
875 						cache->mementries++;
876 
877 #ifdef HAVE_MADVISE
878 						/* ask the kernel to asyncronously read the file from disk so it's cached
879 						   by the time we need it. */
880 						madvise(cachemap->data, cachemap->size, MADV_WILLNEED);
881 #endif				/* HAVE_MADVISE */
882 					}
883 
884 					close(fd);
885 				}
886 			}
887 
888 			if (!(cachemap->flags & CACHE_MEM)) {
889 				cache_invalidate_unlocked(cache, cachemap);
890 				cachemap = NULL;
891 
892 				goto out;
893 			}
894 
895 			cachemap->refcount++;
896 		}
897 	} else if (flags & CACHE_WRITING) {
898 		/* new cache item */
899 		cachemap = cachemap_new(cache);
900 		cachemap->key = xstrdup(key);
901 		cachemap->size = cachemap->realsize = cachemap->offset = 0;
902 		cachemap->mtime = utctime;
903 		cachemap->flags = flags;
904 		cachemap->refcount = 1;
905 
906 		cache_hash_add(cache, cachemap);
907 	}
908 
909 	if (cachemap != NULL) {
910 		cachemap->atime = utctime;
911 
912 		if (!(cachemap->flags & CACHE_WRITING)) {
913 			if (cachemap->header == NULL) {
914 				/* refcount must be 1 at this point, so modifying any cachemap
915 				   member is safe */
916 				cachemap->offset = header_length(cachemap->data, cachemap->size);
917 				if (cachemap->offset != 0)
918 					cachemap->header = http_header_parse_response(cachemap->data);
919 			}
920 		} else {
921 			select_store(connection, cache, cachemap);
922 
923 			/* set header first or we open up a race condition since we unlock
924 			   in cache_add */
925 			cachemap->header = http_header_parse_response(header);
926 			cachemap->offset = strlen(header);
927 
928 			if (cachemap->header->content_length != -1 && cachemap->header->content_length > cache->maxsize) {
929 				putlog(MMLOG_CACHE, "file too large to cache (size: %d)", cachemap->header->content_length);
930 
931 				cache_invalidate_unlocked(cache, cachemap);
932 
933 				cachemap = NULL;
934 
935 				goto out;
936 			}
937 
938 			/* NOTE: cache_add_unlocked unlocks the mutex to relieve contention...
939 			   there may be race conditions calling it from here if you're not careful! */
940 			cache_add_unlocked(cache, cachemap, header, strlen(header));
941 
942 			putlog(MMLOG_DEBUG, "cache header offset: %d", cachemap->offset);
943 		}
944 
945 		if (cachemap->offset == 0 || cachemap->header == NULL || !cacheable(cachemap->header)) {
946 			/* this shouldn't happen, it may be a sign that the cache file was corrupted */
947 			cache_invalidate_unlocked(cache, cachemap);
948 
949 			cachemap = NULL;
950 
951 			putlog(MMLOG_CACHE, "corrupt header: %s", key);
952 
953 			goto out;
954 		}
955 
956 		if (!(cachemap->flags & CACHE_WRITING))
957 			cache_check_header(cache, cachemap);
958 
959 		if (!(flags & CACHE_OFFLINE) && (cachemap->flags & CACHE_VIOLATION) && cache->violaterfc == FALSE) {
960 			cache_close_unlocked(cache, cachemap);
961 			cachemap = NULL;
962 		}
963 
964 	}
965 
966       out:
967 
968 	if (cache->memsize > cache->maxmemsize || (cachemap != NULL && cachemap->store != NULL && cachemap->store->disksize > cachemap->store->maxdisksize))
969 		cache_clean(cache);
970 
971 	pthread_mutex_unlock(&cache->lock);
972 
973 	return cachemap;
974 }
975 
cache_close(CACHE * cache,CACHEMAP * cachemap)976 void cache_close(CACHE * cache, CACHEMAP * cachemap)
977 {
978 	pthread_mutex_lock(&cache->lock);
979 	cache_close_unlocked(cache, cachemap);
980 	pthread_mutex_unlock(&cache->lock);
981 }
982 
cache_close_unlocked(CACHE * cache,CACHEMAP * cachemap)983 void cache_close_unlocked(CACHE * cache, CACHEMAP * cachemap)
984 {
985 	if (cachemap->flags & CACHE_WRITING) {
986 		cachemap->header->content_length = cachemap->size - cachemap->offset;
987 
988 		if (cachemap->size != cachemap->realsize)
989 			cache_resize(cache, cachemap, 0);
990 
991 		msync(cachemap->data, cachemap->realsize, MS_ASYNC);
992 
993 		if (cachemap->fd != -1)
994 			close(cachemap->fd);
995 
996 		cachemap->fd = -1;
997 	}
998 
999 	cachemap->refcount--;
1000 
1001 	if (cachemap->flags & CACHE_INVALID) {
1002 		if (cachemap->refcount == 0)
1003 			cache_invalidate_unlocked(cache, cachemap);
1004 	} else if (cachemap->flags & CACHE_WRITING) {
1005 		if (cachemap->size == 0 && cachemap->size < cache->minsize) {
1006 			if (cachemap->size < cache->minsize)
1007 				putlog(MMLOG_CACHE, "file too small");
1008 
1009 			cache_invalidate_unlocked(cache, cachemap);
1010 		} else {
1011 			cachemap->flags &= ~CACHE_WRITING;
1012 
1013 			/* cache_touch_unlocked does the journal update */
1014 			cache_touch_unlocked(cache, cachemap, TRUE);
1015 
1016 			pthread_cond_broadcast(&cachemap->writecompletecond);
1017 		}
1018 	} else
1019 		cache_touch_unlocked(cache, cachemap, FALSE);
1020 }
1021 
cache_add(CACHE * cache,CACHEMAP * cachemap,void * buf,size_t len)1022 int cache_add(CACHE * cache, CACHEMAP * cachemap, void *buf, size_t len)
1023 {
1024 	int ret;
1025 
1026 	pthread_mutex_lock(&cache->lock);
1027 
1028 	ret = cache_add_unlocked(cache, cachemap, buf, len);
1029 
1030 	pthread_mutex_unlock(&cache->lock);
1031 
1032 	return ret;
1033 }
1034 
cache_add_unlocked(CACHE * cache,CACHEMAP * cachemap,void * buf,size_t len)1035 int cache_add_unlocked(CACHE * cache, CACHEMAP * cachemap, void *buf, size_t len)
1036 {
1037 	int x;
1038 	size_t ret;
1039 	char path[1024], file[1024], *key;
1040 
1041 	if (cachemap->flags & CACHE_INVALID)
1042 		return -1;
1043 
1044 	key = cachemap->key;
1045 
1046 	if (!(cachemap->flags & CACHE_MEM)) {
1047 		if (cachemap->store != NULL) {
1048 			cache_filename(cache, cachemap, file, sizeof(file));
1049 
1050 			x = hash_key(256, key);
1051 			snprintf(path, sizeof(path), "%s/%.2x", cachemap->store->path, x);
1052 
1053 			mkdir(path, 0755);
1054 
1055 			cachemap->fd = open(file, O_RDWR | O_CREAT, 0644);
1056 			if (cachemap->fd != -1) {
1057 				cachemap->store->diskentries++;
1058 				cachemap->flags |= CACHE_DISK;
1059 
1060 				cache_journal_add(cache, cachemap);
1061 			} else
1062 				cachemap->store = NULL;
1063 		}
1064 
1065 		ret = cache_resize(cache, cachemap, len);
1066 		if (ret == 0)
1067 			goto error;
1068 
1069 		memcpy(cachemap->data, buf, len);
1070 	} else {
1071 		ret = cache_resize(cache, cachemap, len);
1072 		if (ret == 0)
1073 			goto error;
1074 
1075 		memcpy(cachemap->data + (cachemap->size - len), buf, len);
1076 
1077 	}
1078 
1079 
1080 	cache->memsize += len;
1081 	if (cachemap->flags & CACHE_DISK)
1082 		cachemap->store->disksize += len;
1083 
1084 	if ((cachemap->store != NULL && cachemap->store->disksize > cachemap->store->maxdisksize) || cache->memsize > cache->maxmemsize)
1085 		cache_clean(cache);
1086 
1087 	if (cache->maxsize != 0 && cachemap->size > cache->maxsize) {
1088 		putlog(MMLOG_CACHE, "max size exceeded (size: %u)", cachemap->size);
1089 
1090 		goto error;
1091 	}
1092 
1093 	if (cachemap->size > cache->maxwaitsize)
1094 		pthread_cond_broadcast(&cachemap->writecompletecond);
1095 
1096 	return len;
1097 
1098       error:
1099 	/* we don't want to free it yet */
1100 	cachemap->refcount++;
1101 	cache_invalidate_unlocked(cache, cachemap);
1102 
1103 	pthread_cond_broadcast(&cachemap->writecompletecond);
1104 
1105 	return -1;
1106 }
1107 
cache_scandisk(CACHE * cache,struct cache_store * store)1108 void cache_scandisk(CACHE * cache, struct cache_store *store)
1109 {
1110 	int x;
1111 	char buf[1024], buf2[1024], *ptr;
1112 	DIR *base, *subdir;
1113 	CACHEMAP *cachemap;
1114 	struct dirent *basedirent, *subdirent;
1115 	struct stat st;
1116 	URL *url;
1117 
1118 	base = opendir(store->path);
1119 	if (base == NULL)
1120 		return;
1121 
1122 	putlog(MMLOG_CACHE, "scanning cache directory structure in %s", store->path);
1123 
1124 	while ((basedirent = readdir(base)) != NULL) {
1125 		if (!strcmp(basedirent->d_name, ".") || !strcmp(basedirent->d_name, ".."))
1126 			continue;
1127 
1128 		snprintf(buf, sizeof(buf), "%s/%s", store->path, basedirent->d_name);
1129 		subdir = opendir(buf);
1130 		if (subdir != NULL) {
1131 			while ((subdirent = readdir(subdir)) != NULL) {
1132 				snprintf(buf, sizeof(buf), "%s/%s/%s", store->path, basedirent->d_name, subdirent->d_name);
1133 				x = stat(buf, &st);
1134 				if (x != -1 && S_ISREG(st.st_mode)) {
1135 					s_strncpy(buf2, subdirent->d_name, sizeof(buf2));
1136 					for (x = 0; buf2[x]; x++)
1137 						if (buf2[x] == '\\')
1138 							buf2[x] = '/';
1139 
1140 
1141 					/* this makes sure it's a valid URL */
1142 					url = url_parse(buf2);
1143 					if (url != NULL) {
1144 						ptr = url_create(url);
1145 						url_free(url);
1146 					} else {
1147 						putlog(MMLOG_CACHE, "%s doesn't appear to be a valid cache file", buf);
1148 						continue;
1149 					}
1150 
1151 					cachemap = cache_hash_find(cache, ptr);
1152 					if (cachemap != NULL) {
1153 						/* this can happen if the unlink thread didn't reach this entry before the proxy
1154 						   server was killed and the file was cached again, or if this directory
1155 						   was used for another instance of the proxy. */
1156 						putlog(MMLOG_CACHE, "duplicate of %s from %s found in %s", ptr, (cachemap->store->path != NULL) ? cachemap->store->path : "(memory)", store->path);
1157 
1158 						/* this could happen if scandisk is called twice on the same directory */
1159 						if (cachemap->store == store) {
1160 							xfree(ptr);
1161 
1162 							continue;
1163 						}
1164 
1165 						/* keep other copy if it's in use or newer */
1166 						if (cachemap->refcount != 0 || cachemap->mtime > st.st_mtime + gmtoffset) {
1167 							unlink_list_add(buf);
1168 
1169 							xfree(ptr);
1170 
1171 							continue;
1172 						}
1173 
1174 						if (cachemap->flags & CACHE_MEM)
1175 							cache_mem_delete(cache, cachemap);
1176 						if (cachemap->flags & CACHE_DISK)
1177 							cache_disk_delete(cache, cachemap);
1178 					}
1179 
1180 					if (cachemap == NULL) {
1181 						cachemap = cachemap_new(cache);
1182 						cachemap->key = ptr;
1183 						cache_hash_add(cache, cachemap);
1184 					} else
1185 						xfree(ptr);
1186 
1187 					cachemap->store = store;
1188 					cachemap->size = cachemap->realsize = st.st_size;
1189 					cachemap->mtime = st.st_mtime + gmtoffset;
1190 					cachemap->atime = st.st_atime + gmtoffset;
1191 					cachemap->flags = CACHE_DISK;
1192 
1193 					store->disksize += st.st_size;
1194 					store->diskentries++;
1195 				}
1196 			}
1197 
1198 			closedir(subdir);
1199 		}
1200 	}
1201 
1202 	closedir(base);
1203 
1204 	return;
1205 }
1206 
cache_clean(CACHE * cache)1207 void cache_clean(CACHE * cache)
1208 {
1209 	int i;
1210 	unsigned int memsizegoal, disksizegoal;
1211 	time_t oldest = 0, newest = 0, threshold, increment, utctime;
1212 	struct CACHEMAPLIST_T *cachemaplist, *tmp;
1213 	CACHEMAP *cachemap;
1214 
1215 	utctime = utc_time(NULL);
1216 
1217 	if (cache->lastclean != 0 && cache->lastclean + CLEAN_INTERVAL > utctime)
1218 		return;
1219 
1220 	cache->lastclean = utctime;
1221 
1222 	putlog(MMLOG_CACHE, "cleaning cache");
1223 
1224 	for (i = 0; i < cache->hashsize; i++) {
1225 		for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = cachemaplist->next) {
1226 			if (cachemaplist->cachemap->refcount != 0)
1227 				continue;
1228 
1229 			if (cachemaplist->cachemap->atime > newest || newest == 0)
1230 				newest = cachemaplist->cachemap->atime;
1231 			if (cachemaplist->cachemap->atime < oldest || oldest == 0)
1232 				oldest = cachemaplist->cachemap->atime;
1233 		}
1234 	}
1235 
1236 	/* oops, nothing we can free */
1237 	if (newest == 0)
1238 		return;
1239 
1240 	increment = (newest - oldest) / CACHE_INCREMENT_FRACTION + 1;
1241 	threshold = oldest + increment;
1242 
1243 	if (cache->memsize > cache->maxmemsize)
1244 		memsizegoal = (cache->memextra > cache->maxmemsize) ? 0 : cache->maxmemsize - cache->memextra;
1245 	else
1246 		memsizegoal = cache->maxmemsize;
1247 
1248 	while (threshold <= newest + increment) {
1249 		for (i = 0; i < cache->hashsize; i++) {
1250 			for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = tmp) {
1251 				tmp = cachemaplist->next;
1252 				cachemap = cachemaplist->cachemap;
1253 
1254 				if (cachemap->refcount != 0)
1255 					continue;
1256 
1257 				if (cachemap->atime > threshold)
1258 					continue;
1259 
1260 				ASSERT(cachemap->flags & (CACHE_DISK | CACHE_MEM));
1261 				ASSERT(!(cachemap->flags & CACHE_DISK) || cachemap->store != NULL);
1262 
1263 				if (cachemap->flags & CACHE_DISK) {
1264 					if (cachemap->store->disksize > cachemap->store->maxdisksize)
1265 						disksizegoal = (cachemap->store->diskextra > cachemap->store->maxdisksize) ? 0 : cachemap->store->maxdisksize - cachemap->store->diskextra;
1266 					else
1267 						disksizegoal = cachemap->store->maxdisksize;
1268 
1269 					if (cachemap->store->disksize > disksizegoal) {
1270 						cache_invalidate_unlocked(cache, cachemap);
1271 						continue;
1272 					}
1273 				}
1274 
1275 				if ((cachemap->flags & CACHE_MEM) && cache->memsize > memsizegoal) {
1276 					if (!(cachemap->flags & CACHE_DISK))
1277 						cache_hash_remove(cache, cachemap);
1278 
1279 					cache_mem_delete(cache, cachemap);
1280 				}
1281 			}
1282 		}
1283 
1284 		/* keep increasing the threshold until enough memory can be free'd */
1285 		threshold += increment;
1286 	}
1287 }
1288 
cacheable(HEADER * header)1289 int cacheable(HEADER * header)
1290 {
1291 	if (header->type == HTTP_RESP) {
1292 		if (header->code != 200)
1293 			return FALSE;
1294 	} else if (header->type == HTTP_PROXY) {
1295 		if (strcasecmp(header->method, "GET"))
1296 			return FALSE;
1297 		if (header->authorization != NULL)
1298 			return FALSE;
1299 		if (header->username != NULL && strcasecmp(header->username, ANONLOGIN))
1300 			return FALSE;
1301 		if (header->range != NULL)
1302 			return FALSE;
1303 	} else
1304 		return FALSE;
1305 
1306 	return TRUE;
1307 }
1308 
1309 /*
1310 check if cache file needs validating based on request headers
1311 */
cache_check(CACHE * cache,HEADER * header)1312 void cache_check(CACHE * cache, HEADER * header)
1313 {
1314 	int x, expired = FALSE, i;
1315 	time_t utctime = utc_time(NULL);
1316 	char buf[1024], **args;
1317 	CACHEMAP *cachemap = NULL;
1318 
1319 	snprintf(buf, sizeof(buf), "%s://%s:%d%s", header->proto, header->host, header->port, header->file);
1320 	cachemap = cache_open(NULL, cache, buf, NULL, CACHE_OFFLINE);
1321 
1322 	if (cachemap == NULL)
1323 		return;
1324 
1325 	pthread_mutex_lock(&cache->lock);
1326 
1327 	if (cachemap->flags & CACHE_PREFETCHED)
1328 		goto out;
1329 
1330 	if (cachemap->flags & CACHE_EXPIRED)
1331 		expired = TRUE;
1332 
1333 	if (header->cache_control != NULL) {
1334 		putlog(MMLOG_DEBUG, "request cache-control header: %s", header->cache_control);
1335 
1336 		args = string_break(header->cache_control, ',');
1337 
1338 		for (x = 0; args[x]; x++) {
1339 			if (!strcasecmp(args[x], "no-cache") || !strcasecmp(args[x], "no-store"))
1340 				expired = TRUE;
1341 			else if (!strncasecmp(args[x], "max-age=", 8)) {
1342 				/* file can't be any older than this */
1343 				i = atoi(&args[x][8]);
1344 
1345 				if (utctime - cachemap->mtime > i)
1346 					expired = TRUE;
1347 			} else if (!strncasecmp(args[x], "min-fresh=", 10)) {
1348 				/* file must be fresh for at least this long */
1349 				i = atoi(&args[x][10]);
1350 
1351 				if ((cachemap->etime != 0 && utctime + i > cachemap->etime) || (cachemap->ftime != 0 && cachemap->etime == 0 && utctime + i > cachemap->ftime))
1352 					expired = TRUE;
1353 			} else if (!strncasecmp(args[x], "max-stale=", 10)) {
1354 				/* file may be stale for this long */
1355 				i = atoi(&args[x][10]);
1356 
1357 				if ((cachemap->etime != 0 && utctime - cachemap->etime <= i) || (cachemap->ftime != 0 && cachemap->etime == 0 && utctime - cachemap->ftime <= i))
1358 					expired = FALSE;
1359 
1360 			}
1361 
1362 			xfree(args[x]);
1363 		}
1364 
1365 		xfree(args);
1366 	}
1367 
1368 	if (expired == TRUE)
1369 		cachemap->flags |= CACHE_VALIDATE;
1370 
1371       out:
1372 
1373 	pthread_mutex_unlock(&cache->lock);
1374 	cache_close(cache, cachemap);
1375 }
1376 
cache_to_filebuf(CACHEMAP * cachemap,int maxlen)1377 FILEBUF *cache_to_filebuf(CACHEMAP * cachemap, int maxlen)
1378 {
1379 	FILEBUF *ret;
1380 
1381 	ret = filebuf_new();
1382 
1383 	filebuf_add(ret, cachemap->data + cachemap->offset, (maxlen < cachemap->size - cachemap->offset) ? maxlen : cachemap->size - cachemap->offset);
1384 
1385 	return ret;
1386 }
1387 
cache_invalidate(CACHE * cache,CACHEMAP * cachemap)1388 void cache_invalidate(CACHE * cache, CACHEMAP * cachemap)
1389 {
1390 	pthread_mutex_lock(&cache->lock);
1391 
1392 	cache_invalidate_unlocked(cache, cachemap);
1393 
1394 	pthread_mutex_unlock(&cache->lock);
1395 }
1396 
cache_invalidate_unlocked(CACHE * cache,CACHEMAP * cachemap)1397 void cache_invalidate_unlocked(CACHE * cache, CACHEMAP * cachemap)
1398 {
1399 	if (!(cachemap->flags & CACHE_INVALID)) {
1400 		putlog(MMLOG_CACHE, "invalidated: %s", cachemap->key);
1401 		cachemap->flags |= CACHE_INVALID;
1402 		cache_journal_add(cache, cachemap);
1403 	}
1404 
1405 	if (cachemap->refcount > 1) {
1406 		cachemap->refcount--;
1407 
1408 		pthread_cond_broadcast(&cachemap->writecompletecond);
1409 
1410 		return;
1411 	}
1412 
1413 	if (cachemap->flags & CACHE_MEM)
1414 		cache_mem_delete(cache, cachemap);
1415 	if (cachemap->flags & CACHE_DISK)
1416 		cache_disk_delete(cache, cachemap);
1417 
1418 	cache_hash_remove(cache, cachemap);
1419 
1420 	cache_free(cache, cachemap);
1421 }
1422 
1423 
cache_disk_delete(CACHE * cache,CACHEMAP * cachemap)1424 void cache_disk_delete(CACHE * cache, CACHEMAP * cachemap)
1425 {
1426 	char buf[1024];
1427 
1428 	ASSERT(cachemap->flags & CACHE_DISK);
1429 	ASSERT(!(cachemap->flags & CACHE_MEM));
1430 
1431 	if (cachemap->store->path != NULL) {
1432 		cache_filename(cache, cachemap, buf, sizeof(buf));
1433 
1434 		unlink_list_add(buf);
1435 	}
1436 
1437 	cachemap->store->disksize -= cachemap->size;
1438 	cachemap->store->diskentries--;
1439 
1440 	cachemap->flags &= ~CACHE_DISK;
1441 }
1442 
cache_mem_delete(CACHE * cache,CACHEMAP * cachemap)1443 void cache_mem_delete(CACHE * cache, CACHEMAP * cachemap)
1444 {
1445 	ASSERT((cachemap->flags & CACHE_MEM) && cachemap->data != NULL);
1446 
1447 	munmap(cachemap->data, cachemap->realsize);
1448 	cache->memsize -= cachemap->size;
1449 	cache->mementries--;
1450 
1451 	cachemap->flags &= ~CACHE_MEM;
1452 	cachemap->data = NULL;
1453 }
1454 
1455 
1456 
cache_touch(CACHE * cache,CACHEMAP * cachemap,int modified)1457 void cache_touch(CACHE * cache, CACHEMAP * cachemap, int modified)
1458 {
1459 	pthread_mutex_lock(&cache->lock);
1460 	cache_touch_unlocked(cache, cachemap, modified);
1461 	pthread_mutex_unlock(&cache->lock);
1462 }
1463 
cache_touch_unlocked(CACHE * cache,CACHEMAP * cachemap,int modified)1464 void cache_touch_unlocked(CACHE * cache, CACHEMAP * cachemap, int modified)
1465 {
1466 	time_t utctime;
1467 	char buf[1024];
1468 	struct utimbuf utb;
1469 
1470 	utctime = utc_time(NULL);
1471 
1472 	cachemap->atime = utctime;
1473 	if (modified == TRUE)
1474 		cachemap->mtime = utctime;
1475 
1476 	if (cachemap->store != NULL) {
1477 		cache_filename(cache, cachemap, buf, sizeof(buf));
1478 
1479 		utb.actime = cachemap->atime - gmtoffset;
1480 		utb.modtime = cachemap->mtime - gmtoffset;
1481 		utime(buf, &utb);
1482 	}
1483 
1484 	cache_journal_add(cache, cachemap);
1485 }
1486 
cache_free(CACHE * cache,CACHEMAP * cachemap)1487 void cache_free(CACHE * cache, CACHEMAP * cachemap)
1488 {
1489 	if (cachemap->fd != -1)
1490 		close(cachemap->fd);
1491 
1492 	if (cachemap->header != NULL)
1493 		http_header_free(cachemap->header);
1494 
1495 	pthread_cond_destroy(&cachemap->writecompletecond);
1496 
1497 	xfree(cachemap->key);
1498 	xfree(cachemap);
1499 }
1500 
cache_flag_set(CACHE * cache,CACHEMAP * cachemap,int flag)1501 int cache_flag_set(CACHE * cache, CACHEMAP * cachemap, int flag)
1502 {
1503 	int ret;
1504 
1505 	pthread_mutex_lock(&cache->lock);
1506 	cachemap->flags |= flag;
1507 	ret = cachemap->flags;
1508 	pthread_mutex_unlock(&cache->lock);
1509 
1510 	return ret;
1511 }
1512 
cache_flag_unset(CACHE * cache,CACHEMAP * cachemap,int flag)1513 int cache_flag_unset(CACHE * cache, CACHEMAP * cachemap, int flag)
1514 {
1515 	int ret;
1516 
1517 	pthread_mutex_lock(&cache->lock);
1518 	cachemap->flags &= ~flag;
1519 	ret = cachemap->flags;
1520 	pthread_mutex_unlock(&cache->lock);
1521 
1522 	return ret;
1523 }
1524 
cache_check_header(CACHE * cache,CACHEMAP * cachemap)1525 void cache_check_header(CACHE * cache, CACHEMAP * cachemap)
1526 {
1527 	int i, x;
1528 	struct tm t;
1529 	char **args;
1530 	time_t utctime = utc_time(NULL);
1531 
1532 	cachemap->ftime = 0;
1533 	cachemap->etime = 0;
1534 	cachemap->lmtime = 0;
1535 	cachemap->header->chunked = FALSE;
1536 	cachemap->flags &= ~CACHE_EXPIRED;
1537 
1538 
1539 	if (cachemap->header->expires != NULL) {
1540 		putlog(MMLOG_DEBUG, "cache file expires header: %s", cachemap->header->expires);
1541 
1542 		memset(&t, 0, sizeof(t));
1543 		strptime(cachemap->header->expires, HTTPTIMEFORMAT, &t);
1544 		cachemap->etime = mktime(&t);
1545 	}
1546 
1547 	if (cachemap->header->last_modified != NULL) {
1548 		putlog(MMLOG_DEBUG, "cache file last-modified header: %s", cachemap->header->last_modified);
1549 
1550 		memset(&t, 0, sizeof(t));
1551 		strptime(cachemap->header->last_modified, HTTPTIMEFORMAT, &t);
1552 		cachemap->lmtime = mktime(&t);
1553 	}
1554 
1555 	/* rfc says Cache-Control takes precedence over Expires: header */
1556 	if (cachemap->header->cache_control != NULL) {
1557 		putlog(MMLOG_DEBUG, "cache file cache-control header: %s", cachemap->header->cache_control);
1558 
1559 		args = string_break(cachemap->header->cache_control, ',');
1560 
1561 		for (x = 0; args[x]; x++) {
1562 			if (!strncasecmp(args[x], "max-age=", 8)) {
1563 				i = atoi(&args[x][8]);
1564 
1565 				cachemap->etime = cachemap->mtime + i;
1566 			} else if (!strcasecmp(args[x], "must-revalidate"))
1567 				cachemap->flags |= CACHE_EXPIRED;
1568 			else if (!strcasecmp(args[x], "no-cache") || !strcasecmp(args[x], "no-store"))
1569 				cachemap->flags |= (CACHE_EXPIRED | CACHE_VIOLATION);
1570 
1571 			xfree(args[x]);
1572 		}
1573 
1574 		xfree(args);
1575 	}
1576 
1577 	if (cachemap->lmtime != 0) {
1578 		/* sanity check */
1579 		if (cachemap->lmtime < cachemap->mtime) {
1580 			/* calculate how long this file should be considered fresh based
1581 			   on a percentage of the delta between the time it was last modified
1582 			   and when it was downloaded or validated; this is used only
1583 			   if the server gives us nothing else to go on.
1584 			 */
1585 			cachemap->ftime = cachemap->mtime + ((cachemap->mtime - cachemap->lmtime) * cache->lmfactor / 100);
1586 
1587 			putlog(MMLOG_DEBUG, "lmtime factor: %u", cachemap->ftime - cachemap->mtime);
1588 		}
1589 	}
1590 
1591 	if ((cachemap->etime == 0 && cachemap->ftime == 0) || (cachemap->mtime + cache->maxage < utctime) || (cachemap->etime != 0 && cachemap->etime < utctime) || (cachemap->ftime != 0 && cachemap->etime == 0 && cachemap->ftime < utctime) || (cachemap->lmtime != 0 && cachemap->lmtime + cache->minage > utctime) || (cachemap->etime == 0 && cachemap->mtime + cache->validate < utctime)) {
1592 
1593 		/* cache file has expired */
1594 		cachemap->flags |= CACHE_EXPIRED;
1595 	}
1596 }
1597 
cache_validated(CACHE * cache,CACHEMAP * cachemap)1598 void cache_validated(CACHE * cache, CACHEMAP * cachemap)
1599 {
1600 	pthread_mutex_lock(&cache->lock);
1601 	cachemap->flags &= ~CACHE_VALIDATE;
1602 	cache_touch_unlocked(cache, cachemap, TRUE);
1603 	pthread_mutex_unlock(&cache->lock);
1604 }
1605 
cache_invalidate_key(CACHE * cache,char * key)1606 int cache_invalidate_key(CACHE * cache, char *key)
1607 {
1608 	int ret = FALSE;
1609 	CACHEMAP *cachemap;
1610 
1611 	pthread_mutex_lock(&cache->lock);
1612 
1613 	cachemap = cache_hash_find(cache, key);
1614 	if (cachemap != NULL) {
1615 		cachemap->refcount++;
1616 		cache_invalidate_unlocked(cache, cachemap);
1617 		ret = TRUE;
1618 	}
1619 
1620 	pthread_mutex_unlock(&cache->lock);
1621 
1622 	return ret;
1623 }
1624 
cache_resize(CACHE * cache,CACHEMAP * cachemap,size_t size)1625 size_t cache_resize(CACHE * cache, CACHEMAP * cachemap, size_t size)
1626 {
1627 	int x;
1628 	void *ptr;
1629 	size_t oldrs;
1630 
1631 	if (size == 0 || (cachemap->size + size > cachemap->realsize)) {
1632 		/* this routine can block under some circumstances, releasing the mutex
1633 		   is safe. */
1634 		pthread_mutex_unlock(&cache->lock);
1635 
1636 		oldrs = cachemap->realsize;
1637 
1638 		if (size == 0)
1639 			cachemap->realsize = cachemap->size;
1640 		else {
1641 			/* if the content length is known, just map the whole thing now */
1642 			if (cachemap->header->content_length != -1 && cachemap->header->content_length + cachemap->offset >= size)
1643 				cachemap->realsize = cachemap->header->content_length + cachemap->offset;
1644 			else {
1645 				/* preallocation tends to hide some errors such as off-by-one's, so we won't do it if this is a debug
1646 				   build. */
1647 #ifdef _DEBUG
1648 				cachemap->realsize = cachemap->size + size;
1649 #else
1650 				cachemap->realsize = ALIGN2(cachemap->size + size, CACHE_ALIGNMENT);
1651 #endif
1652 			}
1653 		}
1654 
1655 		if (cachemap->fd != -1) {
1656 			x = ftruncate(cachemap->fd, cachemap->realsize);
1657 
1658 			if (x == -1) {
1659 				pthread_mutex_lock(&cache->lock);
1660 				goto error;
1661 			}
1662 		}
1663 
1664 		if (!(cachemap->flags & CACHE_MEM))
1665 			ptr = mmap(NULL, cachemap->realsize, PROT_WRITE | PROT_READ, (cachemap->fd != -1) ? MAP_SHARED : MAP_PRIVATE | MAP_ANON, cachemap->fd, 0);
1666 		else
1667 			ptr = p_mremap(cachemap->data, oldrs, cachemap->realsize, cachemap->fd);
1668 
1669 		pthread_mutex_lock(&cache->lock);
1670 
1671 		if (ptr == MAP_FAILED)
1672 			goto error;
1673 		else {
1674 			if (!(cachemap->flags & CACHE_MEM)) {
1675 				cache->mementries++;
1676 				cachemap->flags |= CACHE_MEM;
1677 			}
1678 
1679 			cachemap->data = ptr;
1680 		}
1681 	}
1682 
1683 	cachemap->size += size;
1684 
1685 	return cachemap->size;
1686 
1687       error:
1688 
1689 	return 0;
1690 }
1691 
cache_exists(CACHE * cache,char * key)1692 int cache_exists(CACHE * cache, char *key)
1693 {
1694 	int ret = FALSE;
1695 
1696 	pthread_mutex_lock(&cache->lock);
1697 	if (cache_hash_find(cache, key) != NULL)
1698 		ret = TRUE;
1699 	pthread_mutex_unlock(&cache->lock);
1700 
1701 	return ret;
1702 }
1703 
cache_journal_write(CACHE * cache,struct cache_store * cstore)1704 void cache_journal_write(CACHE * cache, struct cache_store *cstore)
1705 {
1706 	int i;
1707 	char buf[256], buf2[256];
1708 	FILE *fptr;
1709 	CACHEMAP *cachemap;
1710 	struct CACHEMAPLIST_T *cachemaplist;
1711 
1712 	snprintf(buf, sizeof(buf), "%s/journal.new", cstore->path);
1713 	fptr = fopen(buf, "w+");
1714 	if (fptr == NULL)
1715 		return;
1716 
1717 	putlog(MMLOG_CACHE, "writing journal for %s", cstore->path);
1718 
1719 	for (i = 0; i < cache->hashsize; i++) {
1720 		for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = cachemaplist->next) {
1721 			cachemap = cachemaplist->cachemap;
1722 
1723 			if (cachemap->store != NULL && cachemap->store == cstore)
1724 				fprintf(fptr, "%d\t%s\t%u\t%lu\t%lu\n", cachemap->flags, cachemap->key, cachemap->size, cachemap->mtime, cachemap->atime);
1725 		}
1726 	}
1727 
1728 	fclose(fptr);
1729 
1730 	snprintf(buf, sizeof(buf), "%s/journal.new", cstore->path);
1731 	snprintf(buf2, sizeof(buf2), "%s/journal", cstore->path);
1732 
1733 	unlink(buf2);
1734 	rename(buf, buf2);
1735 
1736 	return;
1737 
1738 }
1739 
cache_journal_read(CACHE * cache,struct cache_store * cstore)1740 void cache_journal_read(CACHE * cache, struct cache_store *cstore)
1741 {
1742 	int i;
1743 	char buf[8192], **args;
1744 	size_t size;
1745 	time_t mtime, atime;
1746 	FILE *fptr;
1747 	CACHEMAP *cachemap;
1748 	struct CACHEMAPLIST_T *cachemaplist, *tmp;
1749 
1750 	snprintf(buf, sizeof(buf), "%s/journal", cstore->path);
1751 	fptr = fopen(buf, "r");
1752 	if (fptr == NULL) {
1753 		cache_scandisk(cache, cstore);
1754 		return;
1755 	}
1756 
1757 	putlog(MMLOG_CACHE, "reading journal for %s", cstore->path);
1758 
1759 	/* read each journal entry and determine the last state a cache file was in */
1760 	while (fgets(buf, sizeof(buf), fptr) != NULL) {
1761 		args = string_break(buf, '\t');
1762 		if (array_length(args) == 5) {
1763 			size = strtoul(args[2], NULL, 10);
1764 			mtime = strtoul(args[3], NULL, 10);
1765 			atime = strtoul(args[4], NULL, 10);
1766 
1767 			cachemap = cache_hash_find(cache, args[1]);
1768 			if (cachemap == NULL) {
1769 				cachemap = cachemap_new(cache);
1770 				cachemap->store = cstore;
1771 				cachemap->key = xstrdup(args[1]);
1772 				cache_hash_add(cache, cachemap);
1773 			} else if (cachemap->refcount != 0)
1774 				continue;
1775 			else if (cachemap->store == NULL)
1776 				cachemap->store = cstore;
1777 
1778 			/* we have to deal with the case when the same file shows up in more than one
1779 			   journal (i.e. it was invalidated in one cache store, then later created in another)...
1780 			   we'll use the newest copy */
1781 			if (cachemap->store == cstore || (cachemap->mtime <= mtime && cachemap->store != cstore)) {
1782 				cachemap->size = size;
1783 				cachemap->flags = atoi(args[0]);
1784 				cachemap->mtime = mtime;
1785 				cachemap->atime = atime;
1786 
1787 				cachemap->store = cstore;
1788 			}
1789 		}
1790 
1791 		array_free(args);
1792 	}
1793 
1794 	for (i = 0; i < cache->hashsize; i++) {
1795 		for (cachemaplist = cache->cachemap[i]; cachemaplist; cachemaplist = tmp) {
1796 			tmp = cachemaplist->next;
1797 			cachemap = cachemaplist->cachemap;
1798 
1799 			if (cachemap->store != cstore || cachemap->refcount != 0)
1800 				continue;
1801 
1802 			/* these flags don't carry over between sessions */
1803 			cachemap->flags &= ~CACHE_SESSION_FLAGS;
1804 			/* XXX: ugly hack so redoing cache_journal_read on an active cache works */
1805 			if (cachemap->data != NULL)
1806 				cachemap->flags |= CACHE_MEM;
1807 
1808 			if ((cachemap->flags & (CACHE_WRITING | CACHE_INVALID)) || !(cachemap->flags & CACHE_DISK)) {
1809 				/* the cache object's final state was invalid... remove it. */
1810 
1811 				cache_hash_remove(cache, cachemap);
1812 
1813 				if (cachemap->flags & CACHE_MEM)
1814 					cache_mem_delete(cache, cachemap);
1815 
1816 				if (cachemap->flags & CACHE_DISK) {
1817 					/* cache_disk_delete subtracts from the disksize
1818 					   accounting, but it hasn't been added yet */
1819 					cachemap->store->disksize += cachemap->size;
1820 					cachemap->store->diskentries++;
1821 
1822 					cache_disk_delete(cache, cachemap);
1823 				}
1824 				cache_free(cache, cachemap);
1825 			} else {
1826 				cachemap->store->diskentries++;
1827 				cachemap->store->disksize += cachemap->size;
1828 			}
1829 		}
1830 	}
1831 
1832 	fclose(fptr);
1833 }
1834 
cache_journal_add(CACHE * cache,CACHEMAP * cachemap)1835 int cache_journal_add(CACHE * cache, CACHEMAP * cachemap)
1836 {
1837 	int len;
1838 	char buf[8192];
1839 
1840 	if (cachemap->store == NULL)
1841 		return TRUE;
1842 
1843 	if (cachemap->store->journalfd == -1) {
1844 		snprintf(buf, sizeof(buf), "%s/journal", cachemap->store->path);
1845 		cachemap->store->journalfd = open(buf, O_WRONLY | O_CREAT | O_APPEND);
1846 
1847 		if (cachemap->store->journalfd == -1)
1848 			return -1;
1849 	}
1850 
1851 	len = snprintf(buf, sizeof(buf), "%d\t%s\t%u\t%lu\t%lu\n", cachemap->flags, cachemap->key, cachemap->size, cachemap->mtime, cachemap->atime);
1852 
1853 	write(cachemap->store->journalfd, buf, len);
1854 
1855 	cachemap->store->journalentries++;
1856 
1857 	if (cachemap->store->journalentries >= JOURNAL_SIZE) {
1858 		/* rewrite the journal, this will remove redundant entries */
1859 		close(cachemap->store->journalfd);
1860 		cachemap->store->journalfd = -1;
1861 
1862 		cache_journal_write(cache, cachemap->store);
1863 		cachemap->store->journalentries = 0;
1864 	}
1865 
1866 	return TRUE;
1867 }
1868 
1869 
cachemap_new(CACHE * cache)1870 CACHEMAP *cachemap_new(CACHE * cache)
1871 {
1872 	CACHEMAP *cachemap;
1873 
1874 	cachemap = xmalloc(sizeof(CACHEMAP));
1875 	cachemap->flags = 0;
1876 	cachemap->header = NULL;
1877 	cachemap->fd = -1;
1878 	cachemap->refcount = 0;
1879 	cachemap->data = NULL;
1880 	cachemap->store = NULL;
1881 	pthread_cond_init(&cachemap->writecompletecond, NULL);
1882 
1883 	return cachemap;
1884 }
1885 
unlink_list_add(char * file)1886 void unlink_list_add(char *file)
1887 {
1888 	struct unlink_list *node;
1889 
1890 	node = xmalloc(sizeof(struct unlink_list));
1891 	node->path = xstrdup(file);
1892 
1893 	node->next = NULL;
1894 
1895 	pthread_mutex_lock(&global->unlink_lock);
1896 
1897 	if (global->unlink_tail != NULL)
1898 		global->unlink_tail->next = node;
1899 	global->unlink_tail = node;
1900 
1901 	if (global->unlink_head == NULL)
1902 		global->unlink_head = node;
1903 
1904 	pthread_cond_signal(&global->unlink_cond);
1905 
1906 	pthread_mutex_unlock(&global->unlink_lock);
1907 }
1908 
1909 
unlink_thread()1910 void unlink_thread()
1911 {
1912 	int ret;
1913 	struct unlink_list *tmp;
1914 
1915 	pthread_mutex_lock(&global->unlink_lock);
1916 
1917 	while (1) {
1918 		if (global->unlink_head != NULL) {
1919 			ret = unlink(global->unlink_head->path);
1920 			if (ret == 0)
1921 				putlog(MMLOG_DEBUG, "unlinked %s", global->unlink_head->path);
1922 
1923 			if (global->unlink_head == global->unlink_tail)
1924 				global->unlink_tail = NULL;
1925 
1926 			tmp = global->unlink_head->next;
1927 			xfree(global->unlink_head->path);
1928 			xfree(global->unlink_head);
1929 
1930 			global->unlink_head = tmp;
1931 
1932 			/* ease up on disk activity and give other threads a chance to add
1933 			   items to the unlink list */
1934 			pthread_mutex_unlock(&global->unlink_lock);
1935 			usleep(10000);
1936 			pthread_mutex_lock(&global->unlink_lock);
1937 
1938 			continue;
1939 		}
1940 
1941 		pthread_cond_wait(&global->unlink_cond, &global->unlink_lock);
1942 	}
1943 }
1944