File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / chunk.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:35:00 2016 UTC (7 years, 8 months ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_41p8, HEAD
lighttpd 1.4.41

    1: #include "first.h"
    2: 
    3: /**
    4:  * the network chunk-API
    5:  *
    6:  *
    7:  */
    8: 
    9: #include "chunk.h"
   10: #include "base.h"
   11: #include "log.h"
   12: 
   13: #include <sys/types.h>
   14: #include <sys/stat.h>
   15: #include "sys-mmap.h"
   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: 
   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: 
   32: chunkqueue *chunkqueue_init(void) {
   33: 	chunkqueue *cq;
   34: 
   35: 	cq = calloc(1, sizeof(*cq));
   36: 	force_assert(NULL != cq);
   37: 
   38: 	cq->first = NULL;
   39: 	cq->last = NULL;
   40: 
   41: 	cq->unused = NULL;
   42: 
   43: 	cq->tempdirs              = chunkqueue_default_tempdirs;
   44: 	cq->upload_temp_file_size = chunkqueue_default_tempfile_size;
   45: 
   46: 	return cq;
   47: }
   48: 
   49: static chunk *chunk_init(void) {
   50: 	chunk *c;
   51: 
   52: 	c = calloc(1, sizeof(*c));
   53: 	force_assert(NULL != c);
   54: 
   55: 	c->type = MEM_CHUNK;
   56: 	c->mem = buffer_init();
   57: 	c->file.name = buffer_init();
   58: 	c->file.start = c->file.length = c->file.mmap.offset = 0;
   59: 	c->file.fd = -1;
   60: 	c->file.mmap.start = MAP_FAILED;
   61: 	c->file.mmap.length = 0;
   62: 	c->file.is_temp = 0;
   63: 	c->offset = 0;
   64: 	c->next = NULL;
   65: 
   66: 	return c;
   67: }
   68: 
   69: static void chunk_reset(chunk *c) {
   70: 	if (NULL == c) return;
   71: 
   72: 	c->type = MEM_CHUNK;
   73: 
   74: 	buffer_reset(c->mem);
   75: 
   76: 	if (c->file.is_temp && !buffer_string_is_empty(c->file.name)) {
   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: 	}
   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;
   95: }
   96: 
   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: }
  124: 
  125: void chunkqueue_free(chunkqueue *cq) {
  126: 	chunk *c, *pc;
  127: 
  128: 	if (NULL == cq) return;
  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: 
  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: 
  159: static chunk *chunkqueue_get_unused_chunk(chunkqueue *cq) {
  160: 	chunk *c;
  161: 
  162: 	force_assert(NULL != cq);
  163: 
  164: 	/* check if we have a unused chunk */
  165: 	if (0 == cq->unused) {
  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: 
  178: static void chunkqueue_prepend_chunk(chunkqueue *cq, chunk *c) {
  179: 	c->next = cq->first;
  180: 	cq->first = c;
  181: 
  182: 	if (NULL == cq->last) {
  183: 		cq->last = c;
  184: 	}
  185: 	cq->bytes_in += chunk_remaining_length(c);
  186: }
  187: 
  188: static void chunkqueue_append_chunk(chunkqueue *cq, chunk *c) {
  189: 	c->next = NULL;
  190: 	if (cq->last) {
  191: 		cq->last->next = c;
  192: 	}
  193: 	cq->last = c;
  194: 
  195: 	if (NULL == cq->first) {
  196: 		cq->first = c;
  197: 	}
  198: 	cq->bytes_in += chunk_remaining_length(c);
  199: }
  200: 
  201: void chunkqueue_reset(chunkqueue *cq) {
  202: 	chunk *cur = cq->first;
  203: 
  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;
  210: 	}
  211: 
  212: 	cq->bytes_in = 0;
  213: 	cq->bytes_out = 0;
  214: 	cq->tempdir_idx = 0;
  215: }
  216: 
  217: void chunkqueue_append_file_fd(chunkqueue *cq, buffer *fn, int fd, off_t offset, off_t len) {
  218: 	chunk *c;
  219: 
  220: 	if (0 == len) {
  221: 		close(fd);
  222: 		return;
  223: 	}
  224: 
  225: 	c = chunkqueue_get_unused_chunk(cq);
  226: 
  227: 	c->type = FILE_CHUNK;
  228: 
  229: 	buffer_copy_buffer(c->file.name, fn);
  230: 	c->file.start = offset;
  231: 	c->file.length = len;
  232: 	c->file.fd = fd;
  233: 	c->offset = 0;
  234: 
  235: 	chunkqueue_append_chunk(cq, c);
  236: }
  237: 
  238: void chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) {
  239: 	chunk *c;
  240: 
  241: 	if (0 == len) return;
  242: 
  243: 	c = chunkqueue_get_unused_chunk(cq);
  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;
  250: 	c->offset = 0;
  251: 
  252: 	chunkqueue_append_chunk(cq, c);
  253: }
  254: 
  255: void chunkqueue_append_buffer(chunkqueue *cq, buffer *mem) {
  256: 	chunk *c;
  257: 
  258: 	if (buffer_string_is_empty(mem)) return;
  259: 
  260: 	c = chunkqueue_get_unused_chunk(cq);
  261: 	c->type = MEM_CHUNK;
  262: 	force_assert(NULL != c->mem);
  263: 	buffer_move(c->mem, mem);
  264: 
  265: 	chunkqueue_append_chunk(cq, c);
  266: }
  267: 
  268: void chunkqueue_prepend_buffer(chunkqueue *cq, buffer *mem) {
  269: 	chunk *c;
  270: 
  271: 	if (buffer_string_is_empty(mem)) return;
  272: 
  273: 	c = chunkqueue_get_unused_chunk(cq);
  274: 	c->type = MEM_CHUNK;
  275: 	force_assert(NULL != c->mem);
  276: 	buffer_move(c->mem, mem);
  277: 
  278: 	chunkqueue_prepend_chunk(cq, c);
  279: }
  280: 
  281: 
  282: void chunkqueue_append_mem(chunkqueue *cq, const char * mem, size_t len) {
  283: 	chunk *c;
  284: 
  285: 	if (0 == len) return;
  286: 
  287: 	c = chunkqueue_get_unused_chunk(cq);
  288: 	c->type = MEM_CHUNK;
  289: 	buffer_copy_string_len(c->mem, mem, len);
  290: 
  291: 	chunkqueue_append_chunk(cq, c);
  292: }
  293: 
  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;
  309: }
  310: 
  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;
  314: 	chunk *c;
  315: 	buffer *b;
  316: 	char *dummy_mem;
  317: 	size_t dummy_len;
  318: 
  319: 	force_assert(NULL != cq);
  320: 	if (NULL == mem) mem = &dummy_mem;
  321: 	if (NULL == len) len = &dummy_len;
  322: 
  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;
  327: 
  328: 	if (NULL != cq->last && MEM_CHUNK == cq->last->type) {
  329: 		size_t have;
  330: 
  331: 		b = cq->last->mem;
  332: 		have = buffer_string_space(b);
  333: 
  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: 		}
  351: 
  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: 	}
  359: 
  360: 	/* allocate new chunk */
  361: 	c = chunkqueue_get_unused_chunk(cq);
  362: 	c->type = MEM_CHUNK;
  363: 	chunkqueue_append_chunk(cq, c);
  364: 
  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);
  370: }
  371: 
  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: }
  389: 
  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);
  401: 	cq->tempdirs = tempdirs;
  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: 		}
  425: 
  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: 	}
  454: }
  455: 
  456: static chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) {
  457: 	chunk *c;
  458: 	buffer *template = buffer_init_string("/var/tmp/lighttpd-upload-XXXXXX");
  459: 	int fd = -1;
  460: 
  461: 	if (cq->tempdirs && cq->tempdirs->used) {
  462: 		/* we have several tempdirs, only if all of them fail we jump out */
  463: 
  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];
  466: 
  467: 			buffer_copy_buffer(template, ds->value);
  468: 			buffer_append_slash(template);
  469: 			buffer_append_string_len(template, CONST_STR_LEN("lighttpd-upload-XXXXXX"));
  470: 
  471: 			if (-1 != (fd = mkstemp(template->ptr))) break;
  472: 		}
  473: 	} else {
  474: 		fd = mkstemp(template->ptr);
  475: 	}
  476: 
  477: 	if (fd < 0) {
  478: 		buffer_free(template);
  479: 		return NULL;
  480: 	}
  481: 
  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);
  487: 	c->file.length = 0;
  488: 
  489: 	chunkqueue_append_chunk(cq, c);
  490: 
  491: 	buffer_free(template);
  492: 
  493: 	return c;
  494: }
  495: 
  496: static void chunkqueue_remove_empty_chunks(chunkqueue *cq);
  497: 
  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;
  616: 
  617: 		switch (c->type) {
  618: 		case FILE_CHUNK:
  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: 			}
  632: 			break;
  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: 			}
  650: 			break;
  651: 		}
  652: 
  653: 		src->bytes_out += use;
  654: 	}
  655: 
  656: 	return 0;
  657: }
  658: 
  659: off_t chunkqueue_length(chunkqueue *cq) {
  660: 	off_t len = 0;
  661: 	chunk *c;
  662: 
  663: 	for (c = cq->first; c; c = c->next) {
  664: 		len += chunk_remaining_length(c);
  665: 	}
  666: 
  667: 	return len;
  668: }
  669: 
  670: int chunkqueue_is_empty(chunkqueue *cq) {
  671: 	return NULL == cq->first;
  672: }
  673: 
  674: void chunkqueue_mark_written(chunkqueue *cq, off_t len) {
  675: 	off_t written = len;
  676: 	chunk *c;
  677: 	force_assert(len >= 0);
  678: 
  679: 	for (c = cq->first; NULL != c; c = cq->first) {
  680: 		off_t c_len = chunk_remaining_length(c);
  681: 
  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 */
  696: 		}
  697: 	}
  698: 
  699: 	force_assert(0 == written);
  700: 	cq->bytes_out += len;
  701: }
  702: 
  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 */
  708: 
  709: 		cq->first = c->next;
  710: 		if (c == cq->last) cq->last = NULL;
  711: 
  712: 		chunkqueue_push_unused_chunk(cq, c);
  713: 	}
  714: }
  715: 
  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: 	}
  730: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>