1 #include "links.h"
2 
3 struct list_head cache = {&cache, &cache};
4 
5 my_uintptr_t cache_size = 0;
6 
7 int cache_count = 0;
8 
cache_info(int type)9 my_uintptr_t cache_info(int type)
10 {
11 	my_uintptr_t i = 0;
12 	struct cache_entry *ce;
13 	switch (type) {
14 		case CI_BYTES:
15 			return cache_size;
16 		case CI_FILES:
17 			foreach(ce, cache) i++;
18 			return i;
19 		case CI_LOCKED:
20 			foreach(ce, cache) i += !!ce->refcount;
21 			return i;
22 		case CI_LOADING:
23 			foreach(ce, cache) i += is_entry_used(ce);
24 			return i;
25 		case CI_LIST:
26 			return (my_uintptr_t) &cache;
27 		default:
28 			internal("cache_info: bad request");
29 	}
30 	return 0;
31 }
32 
extract_proxy(unsigned char * url)33 unsigned char *extract_proxy(unsigned char *url)
34 {
35 	char *a;
36 	if (strlen(url) < 8 || casecmp(url, "proxy://", 8)) return url;
37 	if (!(a = strchr(url + 8, '/'))) return url;
38 	return a + 1;
39 }
40 
find_in_cache(unsigned char * url,struct cache_entry ** f)41 int find_in_cache(unsigned char *url, struct cache_entry **f)
42 {
43 	struct cache_entry *e;
44 	url = extract_proxy(url);
45 	foreach(e, cache) if (!strcmp(e->url, url)) {
46 		e->refcount++;
47 		del_from_list(e);
48 		add_to_list(cache, e);
49 		*f = e;
50 		return 0;
51 	}
52 	return -1;
53 }
54 
get_cache_entry(unsigned char * url,struct cache_entry ** f)55 int get_cache_entry(unsigned char *url, struct cache_entry **f)
56 {
57 	struct cache_entry *e;
58 	if (!find_in_cache(url, f)) return 0;
59 	shrink_memory(0);
60 	url = extract_proxy(url);
61 	e = mem_alloc(sizeof(struct cache_entry));
62 	memset(e, 0, sizeof(struct cache_entry));
63 	e->url = mem_alloc(strlen(url) + 1);
64 	strcpy(e->url, url);
65 	e->length = 0;
66 	e->incomplete = 1;
67 	e->data_size = 0;
68 	init_list(e->frag);
69 	e->count = cache_count++;
70 	e->refcount = 1;
71 	add_to_list(cache, e);
72 	*f = e;
73 	return 0;
74 }
75 
76 #define sf(x) e->data_size += (x), cache_size += (x)
77 
78 int page_size = 4096;
79 
80 #define C_ALIGN(x) ((((x) + sizeof(struct fragment) + 64) | (page_size - 1)) - sizeof(struct fragment) - 64)
81 
add_fragment(struct cache_entry * e,off_t offset,unsigned char * data,off_t length)82 int add_fragment(struct cache_entry *e, off_t offset, unsigned char *data, off_t length)
83 {
84 	struct fragment *f;
85 	struct fragment *nf;
86 	int trunc = 0;
87 	volatile off_t ca;
88 	if (!length) return 0;
89 	e->incomplete = 1;
90 	if ((off_t)(0UL + offset + length) < 0 || (off_t)(0UL + offset + length) < offset) overalloc();
91 	if ((off_t)(0UL + offset + (off_t)C_ALIGN(length)) < 0 || (off_t)(0UL + offset + (off_t)C_ALIGN(length)) < offset) overalloc();
92 	if (e->length < offset + length) e->length = offset + length;
93 	e->count = cache_count++;
94 	if (!list_empty(e->frag)) {
95 		f = e->frag.prev;
96 		if (f->offset + f->length == offset) goto have_f;
97 	}
98 	foreach(f, e->frag) {
99 have_f:
100 		if (f->offset > offset) break;
101 		if (f->offset <= offset && f->offset+f->length >= offset) {
102 			if (offset+length > f->offset+f->length) {
103 				if (memcmp(f->data+offset-f->offset, data, f->offset+f->length-offset)) trunc = 1;
104 				if (offset-f->offset+length <= f->real_length) {
105 					sf((offset+length) - (f->offset+f->length));
106 					f->length = offset-f->offset+length;
107 				} else {
108 					sf(-(f->offset+f->length-offset));
109 					f->length = offset-f->offset;
110 					f = f->next;
111 					break;
112 				}
113 			} else {
114 				if (memcmp(f->data+offset-f->offset, data, length)) trunc = 1;
115 			}
116 			memcpy(f->data+offset-f->offset, data, length);
117 			goto ch_o;
118 		}
119 	}
120 /* Intel C 9 has a bug and miscompiles this statement (< 0 test is true) */
121 	/*if (C_ALIGN(length) > MAXINT - sizeof(struct fragment) || C_ALIGN(length) < 0) overalloc();*/
122 	ca = C_ALIGN(length);
123 	if (ca > MAXINT - (int)sizeof(struct fragment) || ca < 0) overalloc();
124 	nf = mem_alloc(sizeof(struct fragment) + ca);
125 	sf(length);
126 	nf->offset = offset;
127 	nf->length = length;
128 	nf->real_length = C_ALIGN(length);
129 	memcpy(nf->data, data, length);
130 	add_at_pos(f->prev, nf);
131 	f = nf;
132 	ch_o:
133 	while ((void *)f->next != &e->frag && f->offset+f->length > f->next->offset) {
134 		if (f->offset+f->length < f->next->offset+f->next->length) {
135 			nf = mem_realloc(f, sizeof(struct fragment)+f->next->offset-f->offset+f->next->length);
136 			nf->prev->next = nf;
137 			nf->next->prev = nf;
138 			f = nf;
139 			if (memcmp(f->data+f->next->offset-f->offset, f->next->data, f->offset+f->length-f->next->offset)) trunc = 1;
140 			memcpy(f->data+f->length, f->next->data+f->offset+f->length-f->next->offset, f->next->offset+f->next->length-f->offset-f->length);
141 			sf(f->next->offset+f->next->length-f->offset-f->length);
142 			f->length = f->real_length = f->next->offset+f->next->length-f->offset;
143 		} else {
144 			if (memcmp(f->data+f->next->offset-f->offset, f->next->data, f->next->length)) trunc = 1;
145 		}
146 		nf = f->next;
147 		del_from_list(nf);
148 		sf(-nf->length);
149 		mem_free(nf);
150 	}
151 	if (trunc) truncate_entry(e, offset + length, 0);
152 	/*{
153 		foreach(f, e->frag) fprintf(stderr, "%d, %d, %d\n", f->offset, f->length, f->real_length);
154 		debug("a-");
155 	}*/
156 	if (e->length > e->max_length) {
157 		e->max_length = e->length;
158 		return 1;
159 	}
160 	return 0;
161 }
162 
defrag_entry(struct cache_entry * e)163 void defrag_entry(struct cache_entry *e)
164 {
165 	struct fragment *f, *g, *h, *n, *x;
166 	off_t l;
167 	if (list_empty(e->frag)) return;
168 	f = e->frag.next;
169 	if (f->offset) return;
170 	for (g = f->next; g != (void *)&e->frag && g->offset <= g->prev->offset+g->prev->length; g = g->next) if (g->offset < g->prev->offset+g->prev->length) {
171 		internal("fragments overlay");
172 		return;
173 	}
174 	if (g == f->next && f->length == f->real_length) return;
175 	for (l = 0, h = f; h != g; h = h->next) {
176 		if ((off_t)(0UL + l + h->length) < 0 || (off_t)(0UL + l + h->length) < l) overalloc();
177 		l += h->length;
178 	}
179 	if (l > MAXINT - (int)sizeof(struct fragment)) overalloc();
180 	n = mem_alloc(sizeof(struct fragment) + l);
181 	n->offset = 0;
182 	n->length = l;
183 	n->real_length = l;
184 	/*{
185 		struct fragment *f;
186 		foreach(f, e->frag) fprintf(stderr, "%d, %d, %d\n", f->offset, f->length, f->real_length);
187 		debug("d1-");
188 	}*/
189 	for (l = 0, h = f; h != g; h = h->next) {
190 		memcpy(n->data + l, h->data, h->length);
191 		l += h->length;
192 		x = h;
193 		h = h->prev;
194 		del_from_list(x);
195 		mem_free(x);
196 	}
197 	add_to_list(e->frag, n);
198 	/*{
199 		foreach(f, e->frag) fprintf(stderr, "%d, %d, %d\n", f->offset, f->length, f->real_length);
200 		debug("d-");
201 	}*/
202 }
203 
truncate_entry(struct cache_entry * e,off_t off,int final)204 void truncate_entry(struct cache_entry *e, off_t off, int final)
205 {
206 	int modified = 0;
207 	struct fragment *f, *g;
208 	if (e->length > off) e->length = off, e->incomplete = 1;
209 	foreach(f, e->frag) {
210 		if (f->offset >= off) {
211 			del:
212 			while ((void *)f != &e->frag) {
213 				modified = 1;
214 				sf(-f->length);
215 				g = f->next;
216 				del_from_list(f);
217 				mem_free(f);
218 				f = g;
219 			}
220 			goto ret;
221 		}
222 		if (f->offset + f->length > off) {
223 			modified = 1;
224 			sf(-(f->offset + f->length - off));
225 			f->length = off - f->offset;
226 			if (final) {
227 				g = mem_realloc(f, sizeof(struct fragment) + f->length);
228 				g->next->prev = g;
229 				g->prev->next = g;
230 				f = g;
231 				f->real_length = f->length;
232 			}
233 			f = f->next;
234 			goto del;
235 		}
236 	}
237 	ret:
238 	if (modified) {
239 		e->count = cache_count++;
240 	}
241 }
242 
free_entry_to(struct cache_entry * e,off_t off)243 void free_entry_to(struct cache_entry *e, off_t off)
244 {
245 	struct fragment *f, *g;
246 	e->incomplete = 1;
247 	foreach(f, e->frag) {
248 		if (f->offset + f->length <= off) {
249 			sf(-f->length);
250 			g = f;
251 			f = f->prev;
252 			del_from_list(g);
253 			mem_free(g);
254 		} else if (f->offset < off) {
255 			sf(f->offset - off);
256 			memmove(f->data, f->data + (off - f->offset), f->length -= off - f->offset);
257 			f->offset = off;
258 		} else break;
259 	}
260 }
261 
delete_entry_content(struct cache_entry * e)262 void delete_entry_content(struct cache_entry *e)
263 {
264 	e->count = cache_count++;
265 	free_list(e->frag);
266 	e->length = 0;
267 	e->incomplete = 1;
268 	if (cache_size < (my_uintptr_t)e->data_size) {
269 		internal("cache_size underflow: %lu, %lu", (unsigned long)cache_size, (unsigned long)e->data_size);
270 	}
271 	cache_size -= e->data_size;
272 	e->data_size = 0;
273 	if (e->last_modified) {
274 		mem_free(e->last_modified);
275 		e->last_modified = NULL;
276 	}
277 }
278 
delete_cache_entry(struct cache_entry * e)279 void delete_cache_entry(struct cache_entry *e)
280 {
281 	if (e->refcount) internal("deleting locked cache entry");
282 #ifdef DEBUG
283 	if (is_entry_used(e)) internal("deleting loading cache entry");
284 #endif
285 	delete_entry_content(e);
286 	del_from_list(e);
287 	mem_free(e->url);
288 	if (e->head) mem_free(e->head);
289 	if (e->last_modified) mem_free(e->last_modified);
290 	if (e->redirect) mem_free(e->redirect);
291 #ifdef HAVE_SSL
292 	if (e->ssl_info) mem_free(e->ssl_info);
293 #endif
294 	mem_free(e);
295 }
296 
garbage_collection(int u)297 void garbage_collection(int u)
298 {
299 	struct cache_entry *e, *f;
300 	my_uintptr_t ncs = cache_size;
301 	my_uintptr_t ccs = 0;
302 	if (!u && cache_size <= (my_uintptr_t)memory_cache_size) return;
303 	foreach(e, cache) {
304 		if (e->refcount || is_entry_used(e)) {
305 			if (ncs < (my_uintptr_t)e->data_size) {
306 				internal("cache_size underflow: %lu, %lu", (unsigned long)ncs, (unsigned long)e->data_size);
307 			}
308 			ncs -= e->data_size;
309 		}
310 		ccs += e->data_size;
311 	}
312 	if (ccs != cache_size) internal("cache size badly computed: %lu != %lu", (unsigned long)cache_size, (unsigned long)ccs), cache_size = ccs;
313 	if (!u && ncs <= (my_uintptr_t)memory_cache_size) return;
314 	for (e = cache.prev; (void *)e != &cache; e = e->prev) {
315 		if (!u && (longlong)ncs <= (longlong)memory_cache_size * MEMORY_CACHE_GC_PERCENT) goto g;
316 		if (e->refcount || is_entry_used(e)) {
317 			e->tgc = 0;
318 			continue;
319 		}
320 		e->tgc = 1;
321 		if (ncs < (my_uintptr_t)e->data_size) {
322 			internal("cache_size underflow: %lu, %lu", (unsigned long)ncs, (unsigned long)e->data_size);
323 		}
324 		ncs -= e->data_size;
325 	}
326 	if (ncs) internal("cache_size(%lu) is larger than size of all objects(%lu)", (unsigned long)cache_size, (unsigned long)(cache_size - ncs));
327 	g:
328 	if ((void *)(e = e->next) == &cache) return;
329 	if (!u) for (f = e; (void *)f != &cache; f = f->next) {
330 		if (f->data_size && (longlong)ncs + f->data_size <= (longlong)memory_cache_size * MEMORY_CACHE_GC_PERCENT) {
331 			ncs += f->data_size;
332 			f->tgc = 0;
333 		}
334 	}
335 	for (f = e; (void *)f != &cache;) {
336 		f = f->next;
337 		if (f->prev->tgc) delete_cache_entry(f->prev);
338 	}
339 }
340 
init_cache(void)341 void init_cache(void)
342 {
343 #ifdef HAVE_GETPAGESIZE
344 	int getpg;
345 	EINTRLOOP(getpg, getpagesize());
346 	if (getpg > 0 && getpg < 0x10000 && !(getpg & (getpg - 1))) page_size = getpg;
347 #endif
348 }
349 
350