Annotation of embedaddon/lighttpd/src/chunk.c, revision 1.1.1.2
1.1.1.2 ! misho 1: #include "first.h"
! 2:
1.1 misho 3: /**
4: * the network chunk-API
5: *
6: *
7: */
8:
9: #include "chunk.h"
1.1.1.2 ! misho 10: #include "base.h"
! 11: #include "log.h"
1.1 misho 12:
13: #include <sys/types.h>
14: #include <sys/stat.h>
1.1.1.2 ! misho 15: #include "sys-mmap.h"
1.1 misho 16:
17: #include <stdlib.h>
18: #include <fcntl.h>
19: #include <unistd.h>
20:
21: #include <stdio.h>
22: #include <errno.h>
23: #include <string.h>
24:
1.1.1.2 ! misho 25: /* default 1MB, upper limit 128MB */
! 26: #define DEFAULT_TEMPFILE_SIZE (1 * 1024 * 1024)
! 27: #define MAX_TEMPFILE_SIZE (128 * 1024 * 1024)
! 28:
! 29: static array *chunkqueue_default_tempdirs = NULL;
! 30: static unsigned int chunkqueue_default_tempfile_size = DEFAULT_TEMPFILE_SIZE;
! 31:
1.1 misho 32: chunkqueue *chunkqueue_init(void) {
33: chunkqueue *cq;
34:
35: cq = calloc(1, sizeof(*cq));
1.1.1.2 ! misho 36: force_assert(NULL != cq);
1.1 misho 37:
38: cq->first = NULL;
39: cq->last = NULL;
40:
41: cq->unused = NULL;
42:
1.1.1.2 ! misho 43: cq->tempdirs = chunkqueue_default_tempdirs;
! 44: cq->upload_temp_file_size = chunkqueue_default_tempfile_size;
! 45:
1.1 misho 46: return cq;
47: }
48:
49: static chunk *chunk_init(void) {
50: chunk *c;
51:
52: c = calloc(1, sizeof(*c));
1.1.1.2 ! misho 53: force_assert(NULL != c);
1.1 misho 54:
1.1.1.2 ! misho 55: c->type = MEM_CHUNK;
1.1 misho 56: c->mem = buffer_init();
57: c->file.name = buffer_init();
1.1.1.2 ! misho 58: c->file.start = c->file.length = c->file.mmap.offset = 0;
1.1 misho 59: c->file.fd = -1;
60: c->file.mmap.start = MAP_FAILED;
1.1.1.2 ! misho 61: c->file.mmap.length = 0;
! 62: c->file.is_temp = 0;
! 63: c->offset = 0;
1.1 misho 64: c->next = NULL;
65:
66: return c;
67: }
68:
69: static void chunk_reset(chunk *c) {
1.1.1.2 ! misho 70: if (NULL == c) return;
! 71:
! 72: c->type = MEM_CHUNK;
1.1 misho 73:
74: buffer_reset(c->mem);
75:
1.1.1.2 ! misho 76: if (c->file.is_temp && !buffer_string_is_empty(c->file.name)) {
1.1 misho 77: unlink(c->file.name->ptr);
78: }
79:
80: buffer_reset(c->file.name);
81:
82: if (c->file.fd != -1) {
83: close(c->file.fd);
84: c->file.fd = -1;
85: }
86: if (MAP_FAILED != c->file.mmap.start) {
87: munmap(c->file.mmap.start, c->file.mmap.length);
88: c->file.mmap.start = MAP_FAILED;
89: }
1.1.1.2 ! misho 90: c->file.start = c->file.length = c->file.mmap.offset = 0;
! 91: c->file.mmap.length = 0;
! 92: c->file.is_temp = 0;
! 93: c->offset = 0;
! 94: c->next = NULL;
1.1 misho 95: }
96:
1.1.1.2 ! misho 97: static void chunk_free(chunk *c) {
! 98: if (NULL == c) return;
! 99:
! 100: chunk_reset(c);
! 101:
! 102: buffer_free(c->mem);
! 103: buffer_free(c->file.name);
! 104:
! 105: free(c);
! 106: }
! 107:
! 108: static off_t chunk_remaining_length(const chunk *c) {
! 109: off_t len = 0;
! 110: switch (c->type) {
! 111: case MEM_CHUNK:
! 112: len = buffer_string_length(c->mem);
! 113: break;
! 114: case FILE_CHUNK:
! 115: len = c->file.length;
! 116: break;
! 117: default:
! 118: force_assert(c->type == MEM_CHUNK || c->type == FILE_CHUNK);
! 119: break;
! 120: }
! 121: force_assert(c->offset <= len);
! 122: return len - c->offset;
! 123: }
1.1 misho 124:
125: void chunkqueue_free(chunkqueue *cq) {
126: chunk *c, *pc;
127:
1.1.1.2 ! misho 128: if (NULL == cq) return;
1.1 misho 129:
130: for (c = cq->first; c; ) {
131: pc = c;
132: c = c->next;
133: chunk_free(pc);
134: }
135:
136: for (c = cq->unused; c; ) {
137: pc = c;
138: c = c->next;
139: chunk_free(pc);
140: }
141:
142: free(cq);
143: }
144:
1.1.1.2 ! misho 145: static void chunkqueue_push_unused_chunk(chunkqueue *cq, chunk *c) {
! 146: force_assert(NULL != cq && NULL != c);
! 147:
! 148: /* keep at max 4 chunks in the 'unused'-cache */
! 149: if (cq->unused_chunks > 4) {
! 150: chunk_free(c);
! 151: } else {
! 152: chunk_reset(c);
! 153: c->next = cq->unused;
! 154: cq->unused = c;
! 155: cq->unused_chunks++;
! 156: }
! 157: }
! 158:
1.1 misho 159: static chunk *chunkqueue_get_unused_chunk(chunkqueue *cq) {
160: chunk *c;
161:
1.1.1.2 ! misho 162: force_assert(NULL != cq);
! 163:
1.1 misho 164: /* check if we have a unused chunk */
1.1.1.2 ! misho 165: if (0 == cq->unused) {
1.1 misho 166: c = chunk_init();
167: } else {
168: /* take the first element from the list (a stack) */
169: c = cq->unused;
170: cq->unused = c->next;
171: c->next = NULL;
172: cq->unused_chunks--;
173: }
174:
175: return c;
176: }
177:
1.1.1.2 ! misho 178: static void chunkqueue_prepend_chunk(chunkqueue *cq, chunk *c) {
1.1 misho 179: c->next = cq->first;
180: cq->first = c;
181:
1.1.1.2 ! misho 182: if (NULL == cq->last) {
1.1 misho 183: cq->last = c;
184: }
1.1.1.2 ! misho 185: cq->bytes_in += chunk_remaining_length(c);
1.1 misho 186: }
187:
1.1.1.2 ! misho 188: static void chunkqueue_append_chunk(chunkqueue *cq, chunk *c) {
! 189: c->next = NULL;
1.1 misho 190: if (cq->last) {
191: cq->last->next = c;
192: }
193: cq->last = c;
194:
1.1.1.2 ! misho 195: if (NULL == cq->first) {
1.1 misho 196: cq->first = c;
197: }
1.1.1.2 ! misho 198: cq->bytes_in += chunk_remaining_length(c);
1.1 misho 199: }
200:
201: void chunkqueue_reset(chunkqueue *cq) {
1.1.1.2 ! misho 202: chunk *cur = cq->first;
1.1 misho 203:
1.1.1.2 ! misho 204: cq->first = cq->last = NULL;
! 205:
! 206: while (NULL != cur) {
! 207: chunk *next = cur->next;
! 208: chunkqueue_push_unused_chunk(cq, cur);
! 209: cur = next;
1.1 misho 210: }
211:
212: cq->bytes_in = 0;
213: cq->bytes_out = 0;
1.1.1.2 ! misho 214: cq->tempdir_idx = 0;
1.1 misho 215: }
216:
1.1.1.2 ! misho 217: void chunkqueue_append_file_fd(chunkqueue *cq, buffer *fn, int fd, off_t offset, off_t len) {
1.1 misho 218: chunk *c;
219:
1.1.1.2 ! misho 220: if (0 == len) {
! 221: close(fd);
! 222: return;
! 223: }
1.1 misho 224:
225: c = chunkqueue_get_unused_chunk(cq);
226:
227: c->type = FILE_CHUNK;
228:
1.1.1.2 ! misho 229: buffer_copy_buffer(c->file.name, fn);
1.1 misho 230: c->file.start = offset;
231: c->file.length = len;
1.1.1.2 ! misho 232: c->file.fd = fd;
1.1 misho 233: c->offset = 0;
234:
235: chunkqueue_append_chunk(cq, c);
236: }
237:
1.1.1.2 ! misho 238: void chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) {
1.1 misho 239: chunk *c;
240:
1.1.1.2 ! misho 241: if (0 == len) return;
1.1 misho 242:
243: c = chunkqueue_get_unused_chunk(cq);
1.1.1.2 ! misho 244:
! 245: c->type = FILE_CHUNK;
! 246:
! 247: buffer_copy_buffer(c->file.name, fn);
! 248: c->file.start = offset;
! 249: c->file.length = len;
1.1 misho 250: c->offset = 0;
251:
252: chunkqueue_append_chunk(cq, c);
253: }
254:
1.1.1.2 ! misho 255: void chunkqueue_append_buffer(chunkqueue *cq, buffer *mem) {
1.1 misho 256: chunk *c;
257:
1.1.1.2 ! misho 258: if (buffer_string_is_empty(mem)) return;
! 259:
1.1 misho 260: c = chunkqueue_get_unused_chunk(cq);
261: c->type = MEM_CHUNK;
1.1.1.2 ! misho 262: force_assert(NULL != c->mem);
! 263: buffer_move(c->mem, mem);
1.1 misho 264:
265: chunkqueue_append_chunk(cq, c);
266: }
267:
1.1.1.2 ! misho 268: void chunkqueue_prepend_buffer(chunkqueue *cq, buffer *mem) {
1.1 misho 269: chunk *c;
270:
1.1.1.2 ! misho 271: if (buffer_string_is_empty(mem)) return;
1.1 misho 272:
273: c = chunkqueue_get_unused_chunk(cq);
274: c->type = MEM_CHUNK;
1.1.1.2 ! misho 275: force_assert(NULL != c->mem);
! 276: buffer_move(c->mem, mem);
1.1 misho 277:
278: chunkqueue_prepend_chunk(cq, c);
279: }
280:
281:
1.1.1.2 ! misho 282: void chunkqueue_append_mem(chunkqueue *cq, const char * mem, size_t len) {
1.1 misho 283: chunk *c;
284:
1.1.1.2 ! misho 285: if (0 == len) return;
1.1 misho 286:
287: c = chunkqueue_get_unused_chunk(cq);
288: c->type = MEM_CHUNK;
1.1.1.2 ! misho 289: buffer_copy_string_len(c->mem, mem, len);
1.1 misho 290:
291: chunkqueue_append_chunk(cq, c);
1.1.1.2 ! misho 292: }
1.1 misho 293:
1.1.1.2 ! misho 294:
! 295: void chunkqueue_append_chunkqueue(chunkqueue *cq, chunkqueue *src) {
! 296: if (src == NULL || NULL == src->first) return;
! 297:
! 298: if (NULL == cq->first) {
! 299: cq->first = src->first;
! 300: } else {
! 301: cq->last->next = src->first;
! 302: }
! 303: cq->last = src->last;
! 304: cq->bytes_in += (src->bytes_in - src->bytes_out);
! 305:
! 306: src->first = NULL;
! 307: src->last = NULL;
! 308: src->bytes_out = src->bytes_in;
1.1 misho 309: }
310:
1.1.1.2 ! misho 311:
! 312: void chunkqueue_get_memory(chunkqueue *cq, char **mem, size_t *len, size_t min_size, size_t alloc_size) {
! 313: static const size_t REALLOC_MAX_SIZE = 256;
1.1 misho 314: chunk *c;
1.1.1.2 ! misho 315: buffer *b;
! 316: char *dummy_mem;
! 317: size_t dummy_len;
1.1 misho 318:
1.1.1.2 ! misho 319: force_assert(NULL != cq);
! 320: if (NULL == mem) mem = &dummy_mem;
! 321: if (NULL == len) len = &dummy_len;
1.1 misho 322:
1.1.1.2 ! misho 323: /* default values: */
! 324: if (0 == min_size) min_size = 1024;
! 325: if (0 == alloc_size) alloc_size = 4096;
! 326: if (alloc_size < min_size) alloc_size = min_size;
1.1 misho 327:
1.1.1.2 ! misho 328: if (NULL != cq->last && MEM_CHUNK == cq->last->type) {
! 329: size_t have;
1.1 misho 330:
1.1.1.2 ! misho 331: b = cq->last->mem;
! 332: have = buffer_string_space(b);
1.1 misho 333:
1.1.1.2 ! misho 334: /* unused buffer: allocate space */
! 335: if (buffer_string_is_empty(b)) {
! 336: buffer_string_prepare_copy(b, alloc_size);
! 337: have = buffer_string_space(b);
! 338: }
! 339: /* if buffer is really small just make it bigger */
! 340: else if (have < min_size && b->size <= REALLOC_MAX_SIZE) {
! 341: size_t cur_len = buffer_string_length(b);
! 342: size_t new_size = cur_len + min_size, append;
! 343: if (new_size < alloc_size) new_size = alloc_size;
! 344:
! 345: append = new_size - cur_len;
! 346: if (append >= min_size) {
! 347: buffer_string_prepare_append(b, append);
! 348: have = buffer_string_space(b);
! 349: }
! 350: }
1.1 misho 351:
1.1.1.2 ! misho 352: /* return pointer into existing buffer if large enough */
! 353: if (have >= min_size) {
! 354: *mem = b->ptr + buffer_string_length(b);
! 355: *len = have;
! 356: return;
! 357: }
! 358: }
1.1 misho 359:
1.1.1.2 ! misho 360: /* allocate new chunk */
! 361: c = chunkqueue_get_unused_chunk(cq);
1.1 misho 362: c->type = MEM_CHUNK;
363: chunkqueue_append_chunk(cq, c);
364:
1.1.1.2 ! misho 365: b = c->mem;
! 366: buffer_string_prepare_append(b, alloc_size);
! 367:
! 368: *mem = b->ptr + buffer_string_length(b);
! 369: *len = buffer_string_space(b);
1.1 misho 370: }
371:
1.1.1.2 ! misho 372: void chunkqueue_use_memory(chunkqueue *cq, size_t len) {
! 373: buffer *b;
! 374:
! 375: force_assert(NULL != cq);
! 376: force_assert(NULL != cq->last && MEM_CHUNK == cq->last->type);
! 377: b = cq->last->mem;
! 378:
! 379: if (len > 0) {
! 380: buffer_commit(b, len);
! 381: cq->bytes_in += len;
! 382: } else if (buffer_string_is_empty(b)) {
! 383: /* unused buffer: can't remove chunk easily from
! 384: * end of list, so just reset the buffer
! 385: */
! 386: buffer_reset(b);
! 387: }
! 388: }
1.1 misho 389:
1.1.1.2 ! misho 390: void chunkqueue_set_tempdirs_default (array *tempdirs, unsigned int upload_temp_file_size) {
! 391: chunkqueue_default_tempdirs = tempdirs;
! 392: chunkqueue_default_tempfile_size
! 393: = (0 == upload_temp_file_size) ? DEFAULT_TEMPFILE_SIZE
! 394: : (upload_temp_file_size > MAX_TEMPFILE_SIZE) ? MAX_TEMPFILE_SIZE
! 395: : upload_temp_file_size;
! 396: }
! 397:
! 398: #if 0
! 399: void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, unsigned int upload_temp_file_size) {
! 400: force_assert(NULL != cq);
1.1 misho 401: cq->tempdirs = tempdirs;
1.1.1.2 ! misho 402: cq->upload_temp_file_size
! 403: = (0 == upload_temp_file_size) ? DEFAULT_TEMPFILE_SIZE
! 404: : (upload_temp_file_size > MAX_TEMPFILE_SIZE) ? MAX_TEMPFILE_SIZE
! 405: : upload_temp_file_size;
! 406: cq->tempdir_idx = 0;
! 407: }
! 408: #endif
! 409:
! 410: void chunkqueue_steal(chunkqueue *dest, chunkqueue *src, off_t len) {
! 411: while (len > 0) {
! 412: chunk *c = src->first;
! 413: off_t clen = 0, use;
! 414:
! 415: if (NULL == c) break;
! 416:
! 417: clen = chunk_remaining_length(c);
! 418: if (0 == clen) {
! 419: /* drop empty chunk */
! 420: src->first = c->next;
! 421: if (c == src->last) src->last = NULL;
! 422: chunkqueue_push_unused_chunk(src, c);
! 423: continue;
! 424: }
1.1 misho 425:
1.1.1.2 ! misho 426: use = len >= clen ? clen : len;
! 427: len -= use;
! 428:
! 429: if (use == clen) {
! 430: /* move complete chunk */
! 431: src->first = c->next;
! 432: if (c == src->last) src->last = NULL;
! 433:
! 434: chunkqueue_append_chunk(dest, c);
! 435: } else {
! 436: /* partial chunk with length "use" */
! 437:
! 438: switch (c->type) {
! 439: case MEM_CHUNK:
! 440: chunkqueue_append_mem(dest, c->mem->ptr + c->offset, use);
! 441: break;
! 442: case FILE_CHUNK:
! 443: /* tempfile flag is in "last" chunk after the split */
! 444: chunkqueue_append_file(dest, c->file.name, c->file.start + c->offset, use);
! 445: break;
! 446: }
! 447:
! 448: c->offset += use;
! 449: force_assert(0 == len);
! 450: }
! 451:
! 452: src->bytes_out += use;
! 453: }
1.1 misho 454: }
455:
1.1.1.2 ! misho 456: static chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) {
1.1 misho 457: chunk *c;
458: buffer *template = buffer_init_string("/var/tmp/lighttpd-upload-XXXXXX");
1.1.1.2 ! misho 459: int fd = -1;
1.1 misho 460:
461: if (cq->tempdirs && cq->tempdirs->used) {
462: /* we have several tempdirs, only if all of them fail we jump out */
463:
1.1.1.2 ! misho 464: for (errno = EIO; cq->tempdir_idx < cq->tempdirs->used; ++cq->tempdir_idx) {
! 465: data_string *ds = (data_string *)cq->tempdirs->data[cq->tempdir_idx];
1.1 misho 466:
1.1.1.2 ! misho 467: buffer_copy_buffer(template, ds->value);
! 468: buffer_append_slash(template);
1.1 misho 469: buffer_append_string_len(template, CONST_STR_LEN("lighttpd-upload-XXXXXX"));
470:
1.1.1.2 ! misho 471: if (-1 != (fd = mkstemp(template->ptr))) break;
1.1 misho 472: }
473: } else {
1.1.1.2 ! misho 474: fd = mkstemp(template->ptr);
! 475: }
! 476:
! 477: if (fd < 0) {
! 478: buffer_free(template);
! 479: return NULL;
1.1 misho 480: }
481:
1.1.1.2 ! misho 482: c = chunkqueue_get_unused_chunk(cq);
! 483: c->type = FILE_CHUNK;
! 484: c->file.fd = fd;
! 485: c->file.is_temp = 1;
! 486: buffer_copy_buffer(c->file.name, template);
1.1 misho 487: c->file.length = 0;
488:
489: chunkqueue_append_chunk(cq, c);
490:
491: buffer_free(template);
492:
493: return c;
494: }
495:
1.1.1.2 ! misho 496: static void chunkqueue_remove_empty_chunks(chunkqueue *cq);
1.1 misho 497:
1.1.1.2 ! misho 498: int chunkqueue_append_mem_to_tempfile(server *srv, chunkqueue *dest, const char *mem, size_t len) {
! 499: chunk *dst_c;
! 500: ssize_t written;
! 501:
! 502: do {
! 503: /*
! 504: * if the last chunk is
! 505: * - smaller than dest->upload_temp_file_size
! 506: * - not read yet (offset == 0)
! 507: * -> append to it (so it might actually become larger than dest->upload_temp_file_size)
! 508: * otherwise
! 509: * -> create a new chunk
! 510: *
! 511: * */
! 512:
! 513: dst_c = dest->last;
! 514: if (NULL != dst_c
! 515: && FILE_CHUNK == dst_c->type
! 516: && dst_c->file.is_temp
! 517: && dst_c->file.fd >= 0
! 518: && 0 == dst_c->offset) {
! 519: /* ok, take the last chunk for our job */
! 520:
! 521: if (dst_c->file.length >= (off_t)dest->upload_temp_file_size) {
! 522: /* the chunk is too large now, close it */
! 523: int rc = close(dst_c->file.fd);
! 524: dst_c->file.fd = -1;
! 525: if (0 != rc) {
! 526: log_error_write(srv, __FILE__, __LINE__, "sbss",
! 527: "close() temp-file", dst_c->file.name, "failed:",
! 528: strerror(errno));
! 529: return -1;
! 530: }
! 531: dst_c = NULL;
! 532: }
! 533: } else {
! 534: dst_c = NULL;
! 535: }
! 536:
! 537: if (NULL == dst_c && NULL == (dst_c = chunkqueue_get_append_tempfile(dest))) {
! 538: /* we don't have file to write to,
! 539: * EACCES might be one reason.
! 540: *
! 541: * Instead of sending 500 we send 413 and say the request is too large
! 542: */
! 543:
! 544: log_error_write(srv, __FILE__, __LINE__, "ss",
! 545: "opening temp-file failed:", strerror(errno));
! 546:
! 547: return -1;
! 548: }
! 549:
! 550: written = write(dst_c->file.fd, mem, len);
! 551:
! 552: if ((size_t) written == len) {
! 553: dst_c->file.length += len;
! 554: dest->bytes_in += len;
! 555:
! 556: return 0;
! 557: } else if (written >= 0) {
! 558: /*(assume EINTR if partial write and retry write();
! 559: * retry write() might fail with ENOSPC if no more space on volume)*/
! 560: dest->bytes_in += written;
! 561: mem += written;
! 562: len -= (size_t)written;
! 563: dst_c->file.length += (size_t)written;
! 564: /* continue; retry */
! 565: } else if (errno == EINTR) {
! 566: /* continue; retry */
! 567: } else {
! 568: int retry = (errno == ENOSPC && dest->tempdirs && ++dest->tempdir_idx < dest->tempdirs->used);
! 569: if (!retry) {
! 570: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 571: "write() temp-file", dst_c->file.name, "failed:",
! 572: strerror(errno));
! 573: }
! 574:
! 575: if (0 == chunk_remaining_length(dst_c)) {
! 576: /*(remove empty chunk and unlink tempfile)*/
! 577: chunkqueue_remove_empty_chunks(dest);
! 578: } else {/*(close tempfile; avoid later attempts to append)*/
! 579: int rc = close(dst_c->file.fd);
! 580: dst_c->file.fd = -1;
! 581: if (0 != rc) {
! 582: log_error_write(srv, __FILE__, __LINE__, "sbss",
! 583: "close() temp-file", dst_c->file.name, "failed:",
! 584: strerror(errno));
! 585: return -1;
! 586: }
! 587: }
! 588: if (!retry) return -1;
! 589:
! 590: /* continue; retry */
! 591: }
! 592:
! 593: } while (dst_c);
! 594:
! 595: return -1; /*(not reached)*/
! 596: }
! 597:
! 598: int chunkqueue_steal_with_tempfiles(server *srv, chunkqueue *dest, chunkqueue *src, off_t len) {
! 599: while (len > 0) {
! 600: chunk *c = src->first;
! 601: off_t clen = 0, use;
! 602:
! 603: if (NULL == c) break;
! 604:
! 605: clen = chunk_remaining_length(c);
! 606: if (0 == clen) {
! 607: /* drop empty chunk */
! 608: src->first = c->next;
! 609: if (c == src->last) src->last = NULL;
! 610: chunkqueue_push_unused_chunk(src, c);
! 611: continue;
! 612: }
! 613:
! 614: use = (len >= clen) ? clen : len;
! 615: len -= use;
1.1 misho 616:
617: switch (c->type) {
618: case FILE_CHUNK:
1.1.1.2 ! misho 619: if (use == clen) {
! 620: /* move complete chunk */
! 621: src->first = c->next;
! 622: if (c == src->last) src->last = NULL;
! 623: chunkqueue_append_chunk(dest, c);
! 624: } else {
! 625: /* partial chunk with length "use" */
! 626: /* tempfile flag is in "last" chunk after the split */
! 627: chunkqueue_append_file(dest, c->file.name, c->file.start + c->offset, use);
! 628:
! 629: c->offset += use;
! 630: force_assert(0 == len);
! 631: }
1.1 misho 632: break;
1.1.1.2 ! misho 633:
! 634: case MEM_CHUNK:
! 635: /* store "use" bytes from memory chunk in tempfile */
! 636: if (0 != chunkqueue_append_mem_to_tempfile(srv, dest, c->mem->ptr + c->offset, use)) {
! 637: return -1;
! 638: }
! 639:
! 640: if (use == clen) {
! 641: /* finished chunk */
! 642: src->first = c->next;
! 643: if (c == src->last) src->last = NULL;
! 644: chunkqueue_push_unused_chunk(src, c);
! 645: } else {
! 646: /* partial chunk */
! 647: c->offset += use;
! 648: force_assert(0 == len);
! 649: }
1.1 misho 650: break;
651: }
1.1.1.2 ! misho 652:
! 653: src->bytes_out += use;
1.1 misho 654: }
655:
1.1.1.2 ! misho 656: return 0;
1.1 misho 657: }
658:
1.1.1.2 ! misho 659: off_t chunkqueue_length(chunkqueue *cq) {
1.1 misho 660: off_t len = 0;
661: chunk *c;
662:
663: for (c = cq->first; c; c = c->next) {
1.1.1.2 ! misho 664: len += chunk_remaining_length(c);
1.1 misho 665: }
666:
667: return len;
668: }
669:
670: int chunkqueue_is_empty(chunkqueue *cq) {
1.1.1.2 ! misho 671: return NULL == cq->first;
1.1 misho 672: }
673:
1.1.1.2 ! misho 674: void chunkqueue_mark_written(chunkqueue *cq, off_t len) {
! 675: off_t written = len;
1.1 misho 676: chunk *c;
1.1.1.2 ! misho 677: force_assert(len >= 0);
1.1 misho 678:
1.1.1.2 ! misho 679: for (c = cq->first; NULL != c; c = cq->first) {
! 680: off_t c_len = chunk_remaining_length(c);
1.1 misho 681:
1.1.1.2 ! misho 682: if (0 == written && 0 != c_len) break; /* no more finished chunks */
! 683:
! 684: if (written >= c_len) { /* chunk got finished */
! 685: c->offset += c_len;
! 686: written -= c_len;
! 687:
! 688: cq->first = c->next;
! 689: if (c == cq->last) cq->last = NULL;
! 690:
! 691: chunkqueue_push_unused_chunk(cq, c);
! 692: } else { /* partial chunk */
! 693: c->offset += written;
! 694: written = 0;
! 695: break; /* chunk not finished */
1.1 misho 696: }
1.1.1.2 ! misho 697: }
1.1 misho 698:
1.1.1.2 ! misho 699: force_assert(0 == written);
! 700: cq->bytes_out += len;
! 701: }
1.1 misho 702:
1.1.1.2 ! misho 703: void chunkqueue_remove_finished_chunks(chunkqueue *cq) {
! 704: chunk *c;
! 705:
! 706: for (c = cq->first; c; c = cq->first) {
! 707: if (0 != chunk_remaining_length(c)) break; /* not finished yet */
1.1 misho 708:
709: cq->first = c->next;
710: if (c == cq->last) cq->last = NULL;
711:
1.1.1.2 ! misho 712: chunkqueue_push_unused_chunk(cq, c);
1.1 misho 713: }
1.1.1.2 ! misho 714: }
1.1 misho 715:
1.1.1.2 ! misho 716: static void chunkqueue_remove_empty_chunks(chunkqueue *cq) {
! 717: chunk *c;
! 718: chunkqueue_remove_finished_chunks(cq);
! 719: if (chunkqueue_is_empty(cq)) return;
! 720:
! 721: for (c = cq->first; c->next; c = c->next) {
! 722: if (0 == chunk_remaining_length(c->next)) {
! 723: chunk *empty = c->next;
! 724: c->next = empty->next;
! 725: if (empty == cq->last) cq->last = c;
! 726:
! 727: chunkqueue_push_unused_chunk(cq, empty);
! 728: }
! 729: }
1.1 misho 730: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>