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