1 /**
2  * the network chunk-API
3  *
4  *
5  */
6 
7 #include "chunk.h"
8 
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <sys/mman.h>
12 
13 #include <stdlib.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 
17 #include <stdio.h>
18 #include <errno.h>
19 #include <string.h>
20 
chunkqueue_init(void)21 chunkqueue *chunkqueue_init(void) {
22 	chunkqueue *cq;
23 
24 	cq = calloc(1, sizeof(*cq));
25 
26 	cq->first = NULL;
27 	cq->last = NULL;
28 
29 	cq->unused = NULL;
30 
31 	return cq;
32 }
33 
chunk_init(void)34 static chunk *chunk_init(void) {
35 	chunk *c;
36 
37 	c = calloc(1, sizeof(*c));
38 
39 	c->mem = buffer_init();
40 	c->file.name = buffer_init();
41 	c->file.fd = -1;
42 	c->file.mmap.start = MAP_FAILED;
43 	c->next = NULL;
44 
45 	return c;
46 }
47 
chunk_free(chunk * c)48 static void chunk_free(chunk *c) {
49 	if (!c) return;
50 
51 	buffer_free(c->mem);
52 	buffer_free(c->file.name);
53 
54 	free(c);
55 }
56 
chunk_reset(chunk * c)57 static void chunk_reset(chunk *c) {
58 	if (!c) return;
59 
60 	buffer_reset(c->mem);
61 
62 	if (c->file.is_temp && !buffer_is_empty(c->file.name)) {
63 		unlink(c->file.name->ptr);
64 	}
65 
66 	buffer_reset(c->file.name);
67 
68 	if (c->file.fd != -1) {
69 		close(c->file.fd);
70 		c->file.fd = -1;
71 	}
72 	if (MAP_FAILED != c->file.mmap.start) {
73 		munmap(c->file.mmap.start, c->file.mmap.length);
74 		c->file.mmap.start = MAP_FAILED;
75 	}
76 }
77 
78 
chunkqueue_free(chunkqueue * cq)79 void chunkqueue_free(chunkqueue *cq) {
80 	chunk *c, *pc;
81 
82 	if (!cq) return;
83 
84 	for (c = cq->first; c; ) {
85 		pc = c;
86 		c = c->next;
87 		chunk_free(pc);
88 	}
89 
90 	for (c = cq->unused; c; ) {
91 		pc = c;
92 		c = c->next;
93 		chunk_free(pc);
94 	}
95 
96 	free(cq);
97 }
98 
chunkqueue_get_unused_chunk(chunkqueue * cq)99 static chunk *chunkqueue_get_unused_chunk(chunkqueue *cq) {
100 	chunk *c;
101 
102 	/* check if we have a unused chunk */
103 	if (!cq->unused) {
104 		c = chunk_init();
105 	} else {
106 		/* take the first element from the list (a stack) */
107 		c = cq->unused;
108 		cq->unused = c->next;
109 		c->next = NULL;
110 		cq->unused_chunks--;
111 	}
112 
113 	return c;
114 }
115 
chunkqueue_prepend_chunk(chunkqueue * cq,chunk * c)116 static int chunkqueue_prepend_chunk(chunkqueue *cq, chunk *c) {
117 	c->next = cq->first;
118 	cq->first = c;
119 
120 	if (cq->last == NULL) {
121 		cq->last = c;
122 	}
123 
124 	return 0;
125 }
126 
chunkqueue_append_chunk(chunkqueue * cq,chunk * c)127 static int chunkqueue_append_chunk(chunkqueue *cq, chunk *c) {
128 	if (cq->last) {
129 		cq->last->next = c;
130 	}
131 	cq->last = c;
132 
133 	if (cq->first == NULL) {
134 		cq->first = c;
135 	}
136 
137 	return 0;
138 }
139 
chunkqueue_reset(chunkqueue * cq)140 void chunkqueue_reset(chunkqueue *cq) {
141 	chunk *c;
142 	/* move everything to the unused queue */
143 
144 	/* mark all read written */
145 	for (c = cq->first; c; c = c->next) {
146 		switch(c->type) {
147 		case MEM_CHUNK:
148 			c->offset = c->mem->used - 1;
149 			break;
150 		case FILE_CHUNK:
151 			c->offset = c->file.length;
152 			break;
153 		default:
154 			break;
155 		}
156 	}
157 
158 	chunkqueue_remove_finished_chunks(cq);
159 	cq->bytes_in = 0;
160 	cq->bytes_out = 0;
161 }
162 
chunkqueue_append_file(chunkqueue * cq,buffer * fn,off_t offset,off_t len)163 int chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) {
164 	chunk *c;
165 
166 	if (len == 0) return 0;
167 
168 	c = chunkqueue_get_unused_chunk(cq);
169 
170 	c->type = FILE_CHUNK;
171 
172 	buffer_copy_string_buffer(c->file.name, fn);
173 	c->file.start = offset;
174 	c->file.length = len;
175 	c->offset = 0;
176 
177 	chunkqueue_append_chunk(cq, c);
178 
179 	return 0;
180 }
181 
chunkqueue_append_buffer(chunkqueue * cq,buffer * mem)182 int chunkqueue_append_buffer(chunkqueue *cq, buffer *mem) {
183 	chunk *c;
184 
185 	if (mem->used == 0) return 0;
186 
187 	c = chunkqueue_get_unused_chunk(cq);
188 	c->type = MEM_CHUNK;
189 	c->offset = 0;
190 	buffer_copy_string_buffer(c->mem, mem);
191 
192 	chunkqueue_append_chunk(cq, c);
193 
194 	return 0;
195 }
196 
chunkqueue_append_buffer_weak(chunkqueue * cq,buffer * mem)197 int chunkqueue_append_buffer_weak(chunkqueue *cq, buffer *mem) {
198 	chunk *c;
199 
200 	c = chunkqueue_get_unused_chunk(cq);
201 	c->type = MEM_CHUNK;
202 	c->offset = 0;
203 	if (c->mem) buffer_free(c->mem);
204 	c->mem = mem;
205 
206 	chunkqueue_append_chunk(cq, c);
207 
208 	return 0;
209 }
210 
chunkqueue_prepend_buffer(chunkqueue * cq,buffer * mem)211 int chunkqueue_prepend_buffer(chunkqueue *cq, buffer *mem) {
212 	chunk *c;
213 
214 	if (mem->used == 0) return 0;
215 
216 	c = chunkqueue_get_unused_chunk(cq);
217 	c->type = MEM_CHUNK;
218 	c->offset = 0;
219 	buffer_copy_string_buffer(c->mem, mem);
220 
221 	chunkqueue_prepend_chunk(cq, c);
222 
223 	return 0;
224 }
225 
226 
chunkqueue_append_mem(chunkqueue * cq,const char * mem,size_t len)227 int chunkqueue_append_mem(chunkqueue *cq, const char * mem, size_t len) {
228 	chunk *c;
229 
230 	if (len == 0) return 0;
231 
232 	c = chunkqueue_get_unused_chunk(cq);
233 	c->type = MEM_CHUNK;
234 	c->offset = 0;
235 	buffer_copy_string_len(c->mem, mem, len - 1);
236 
237 	chunkqueue_append_chunk(cq, c);
238 
239 	return 0;
240 }
241 
chunkqueue_get_prepend_buffer(chunkqueue * cq)242 buffer * chunkqueue_get_prepend_buffer(chunkqueue *cq) {
243 	chunk *c;
244 
245 	c = chunkqueue_get_unused_chunk(cq);
246 
247 	c->type = MEM_CHUNK;
248 	c->offset = 0;
249 	buffer_reset(c->mem);
250 
251 	chunkqueue_prepend_chunk(cq, c);
252 
253 	return c->mem;
254 }
255 
chunkqueue_get_append_buffer(chunkqueue * cq)256 buffer *chunkqueue_get_append_buffer(chunkqueue *cq) {
257 	chunk *c;
258 
259 	c = chunkqueue_get_unused_chunk(cq);
260 
261 	c->type = MEM_CHUNK;
262 	c->offset = 0;
263 	buffer_reset(c->mem);
264 
265 	chunkqueue_append_chunk(cq, c);
266 
267 	return c->mem;
268 }
269 
chunkqueue_set_tempdirs(chunkqueue * cq,array * tempdirs)270 int chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs) {
271 	if (!cq) return -1;
272 
273 	cq->tempdirs = tempdirs;
274 
275 	return 0;
276 }
277 
chunkqueue_get_append_tempfile(chunkqueue * cq)278 chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) {
279 	chunk *c;
280 	buffer *template = buffer_init_string("/var/tmp/lighttpd-upload-XXXXXX");
281 
282 	c = chunkqueue_get_unused_chunk(cq);
283 
284 	c->type = FILE_CHUNK;
285 	c->offset = 0;
286 
287 	if (cq->tempdirs && cq->tempdirs->used) {
288 		size_t i;
289 
290 		/* we have several tempdirs, only if all of them fail we jump out */
291 
292 		for (i = 0; i < cq->tempdirs->used; i++) {
293 			data_string *ds = (data_string *)cq->tempdirs->data[i];
294 
295 			buffer_copy_string_buffer(template, ds->value);
296 			BUFFER_APPEND_SLASH(template);
297 			buffer_append_string_len(template, CONST_STR_LEN("lighttpd-upload-XXXXXX"));
298 
299 			if (-1 != (c->file.fd = mkstemp(template->ptr))) {
300 				/* only trigger the unlink if we created the temp-file successfully */
301 				c->file.is_temp = 1;
302 				break;
303 			}
304 		}
305 	} else {
306 		if (-1 != (c->file.fd = mkstemp(template->ptr))) {
307 			/* only trigger the unlink if we created the temp-file successfully */
308 			c->file.is_temp = 1;
309 		}
310 	}
311 
312 	buffer_copy_string_buffer(c->file.name, template);
313 	c->file.length = 0;
314 
315 	chunkqueue_append_chunk(cq, c);
316 
317 	buffer_free(template);
318 
319 	return c;
320 }
321 
322 
chunkqueue_length(chunkqueue * cq)323 off_t chunkqueue_length(chunkqueue *cq) {
324 	off_t len = 0;
325 	chunk *c;
326 
327 	for (c = cq->first; c; c = c->next) {
328 		switch (c->type) {
329 		case MEM_CHUNK:
330 			len += c->mem->used ? c->mem->used - 1 : 0;
331 			break;
332 		case FILE_CHUNK:
333 			len += c->file.length;
334 			break;
335 		default:
336 			break;
337 		}
338 	}
339 
340 	return len;
341 }
342 
chunkqueue_written(chunkqueue * cq)343 off_t chunkqueue_written(chunkqueue *cq) {
344 	off_t len = 0;
345 	chunk *c;
346 
347 	for (c = cq->first; c; c = c->next) {
348 		switch (c->type) {
349 		case MEM_CHUNK:
350 		case FILE_CHUNK:
351 			len += c->offset;
352 			break;
353 		default:
354 			break;
355 		}
356 	}
357 
358 	return len;
359 }
360 
chunkqueue_is_empty(chunkqueue * cq)361 int chunkqueue_is_empty(chunkqueue *cq) {
362 	return cq->first ? 0 : 1;
363 }
364 
chunkqueue_remove_finished_chunks(chunkqueue * cq)365 int chunkqueue_remove_finished_chunks(chunkqueue *cq) {
366 	chunk *c;
367 
368 	for (c = cq->first; c; c = cq->first) {
369 		int is_finished = 0;
370 
371 		switch (c->type) {
372 		case MEM_CHUNK:
373 			if (c->mem->used == 0 || (c->offset == (off_t)c->mem->used - 1)) is_finished = 1;
374 			break;
375 		case FILE_CHUNK:
376 			if (c->offset == c->file.length) is_finished = 1;
377 			break;
378 		default:
379 			break;
380 		}
381 
382 		if (!is_finished) break;
383 
384 		chunk_reset(c);
385 
386 		cq->first = c->next;
387 		if (c == cq->last) cq->last = NULL;
388 
389 		/* keep at max 4 chunks in the 'unused'-cache */
390 		if (cq->unused_chunks > 4) {
391 			chunk_free(c);
392 		} else {
393 			c->next = cq->unused;
394 			cq->unused = c;
395 			cq->unused_chunks++;
396 		}
397 	}
398 
399 	return 0;
400 }
401