/* get the data from a chunk; easy in case of a STRING_CHUNK, * but needs to do io in case of FILE_CHUNK; the data is _not_ marked as "done" * may return HANDLER_GO_ON, HANDLER_ERROR */ liHandlerResult li_chunkiter_read(liVRequest *vr, liChunkIter iter, off_t start, off_t length, char **data_start, off_t *data_len) { liChunk *c = li_chunkiter_chunk(iter); off_t we_have, our_start; liHandlerResult res = LI_HANDLER_GO_ON; if (!c) return LI_HANDLER_ERROR; if (!data_start || !data_len) return LI_HANDLER_ERROR; we_have = li_chunk_length(c) - start; if (length > we_have) length = we_have; if (length <= 0) return LI_HANDLER_ERROR; switch (c->type) { case UNUSED_CHUNK: return LI_HANDLER_ERROR; case STRING_CHUNK: *data_start = c->data.str->str + c->offset + start; *data_len = length; break; case MEM_CHUNK: *data_start = (char*) c->mem->data + c->offset + start; *data_len = length; break; case FILE_CHUNK: if (LI_HANDLER_GO_ON != (res = li_chunkfile_open(vr, c->data.file.file))) return res; if (length > MAX_MMAP_CHUNK) length = MAX_MMAP_CHUNK; if (!c->mem) { c->mem = g_byte_array_sized_new(length); } else { g_byte_array_set_size(c->mem, length); } our_start = start + c->offset + c->data.file.start; read_chunk: if (-1 == (we_have = pread(c->data.file.file->fd, c->mem->data, length, our_start))) { if (EINTR == errno) goto read_chunk; VR_ERROR(vr, "pread failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd, g_strerror(errno)); g_byte_array_free(c->mem, TRUE); c->mem = NULL; return LI_HANDLER_ERROR; } else if (we_have != length) { /* may return less than requested bytes due to signals */ /* CON_TRACE(srv, "read return unexpected number of bytes"); */ if (we_have == 0) { VR_ERROR(vr, "pread returned 0 bytes for '%s' (fd = %i): unexpected end of file?", GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd); g_byte_array_free(c->mem, TRUE); c->mem = NULL; return LI_HANDLER_ERROR; } length = we_have; g_byte_array_set_size(c->mem, length); } *data_start = (char*) c->mem->data; *data_len = length; break; case BUFFER_CHUNK: *data_start = (char*) c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset + start; *data_len = length; break; } return LI_HANDLER_GO_ON; }
/* first chunk must be a STRING_CHUNK ! */ liNetworkStatus li_network_backend_writev(int fd, liChunkQueue *cq, goffset *write_max, GError **err) { off_t we_have; ssize_t r; gboolean did_write_something = FALSE; liChunkIter ci; liChunk *c; liNetworkStatus res = LI_NETWORK_STATUS_FATAL_ERROR; GArray *chunks = g_array_sized_new(FALSE, TRUE, sizeof(struct iovec), UIO_MAXIOV); if (0 == cq->length) goto cleanup; /* FATAL ERROR */ do { ci = li_chunkqueue_iter(cq); if (STRING_CHUNK != (c = li_chunkiter_chunk(ci))->type && MEM_CHUNK != c->type && BUFFER_CHUNK != c->type) { res = did_write_something ? LI_NETWORK_STATUS_SUCCESS : LI_NETWORK_STATUS_FATAL_ERROR; goto cleanup; } we_have = 0; do { guint i = chunks->len; off_t len = li_chunk_length(c); struct iovec *v; g_array_set_size(chunks, i + 1); v = &g_array_index(chunks, struct iovec, i); if (c->type == STRING_CHUNK) { v->iov_base = c->data.str->str + c->offset; } else if (c->type == MEM_CHUNK) { v->iov_base = c->mem->data + c->offset; } else { /* if (c->type == BUFFER_CHUNK) */ v->iov_base = c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset; } if (len > *write_max - we_have) len = *write_max - we_have; v->iov_len = len; we_have += len; } while (we_have < *write_max && li_chunkiter_next(&ci) && (STRING_CHUNK == (c = li_chunkiter_chunk(ci))->type || MEM_CHUNK == c->type || BUFFER_CHUNK == c->type) && chunks->len < UIO_MAXIOV); while (-1 == (r = writev(fd, (struct iovec*) chunks->data, chunks->len))) { switch (errno) { case EAGAIN: #if EWOULDBLOCK != EAGAIN case EWOULDBLOCK: #endif res = LI_NETWORK_STATUS_WAIT_FOR_EVENT; goto cleanup; case ECONNRESET: case EPIPE: case ETIMEDOUT: res = LI_NETWORK_STATUS_CONNECTION_CLOSE; goto cleanup; case EINTR: break; /* try again */ default: g_set_error(err, LI_NETWORK_ERROR, 0, "li_network_backend_writev: oops, write to fd=%d failed: %s", fd, g_strerror(errno)); goto cleanup; } } if (0 == r) { res = LI_NETWORK_STATUS_WAIT_FOR_EVENT; goto cleanup; } li_chunkqueue_skip(cq, r); *write_max -= r; if (r != we_have) { res = LI_NETWORK_STATUS_WAIT_FOR_EVENT; goto cleanup; } if (0 == cq->length) { res = LI_NETWORK_STATUS_SUCCESS; goto cleanup; } did_write_something = TRUE; g_array_set_size(chunks, 0); } while (*write_max > 0); res = LI_NETWORK_STATUS_SUCCESS; cleanup: g_array_free(chunks, TRUE); return res; }
/* same as li_chunkiter_read, but tries mmap() first and falls back to pread(); * as accessing mmap()-ed areas may result in SIGBUS, you have to handle that signal somehow. */ liHandlerResult li_chunkiter_read_mmap(liVRequest *vr, liChunkIter iter, off_t start, off_t length, char **data_start, off_t *data_len) { liChunk *c = li_chunkiter_chunk(iter); off_t we_want, we_have, our_start, our_offset; liHandlerResult res = LI_HANDLER_GO_ON; int mmap_errno = 0; if (!c) return LI_HANDLER_ERROR; if (!data_start || !data_len) return LI_HANDLER_ERROR; we_have = li_chunk_length(c) - start; if (length > we_have) length = we_have; if (length <= 0) return LI_HANDLER_ERROR; switch (c->type) { case UNUSED_CHUNK: return LI_HANDLER_ERROR; case STRING_CHUNK: *data_start = c->data.str->str + c->offset + start; *data_len = length; break; case MEM_CHUNK: *data_start = (char*) c->mem->data + c->offset + start; *data_len = length; break; case FILE_CHUNK: if (LI_HANDLER_GO_ON != (res = li_chunkfile_open(vr, c->data.file.file))) return res; if (length > MAX_MMAP_CHUNK) length = MAX_MMAP_CHUNK; if ( !(c->data.file.mmap.data != MAP_FAILED || c->mem) /* no data present */ || !( /* or in the wrong range */ (start + c->offset + c->data.file.start >= c->data.file.mmap.offset) && (start + c->offset + c->data.file.start + length <= c->data.file.mmap.offset + (ssize_t) c->data.file.mmap.length)) ) { /* then find new range */ our_start = start + c->offset + c->data.file.start; /* "start" maps to this offset in the file */ our_offset = our_start % MMAP_CHUNK_ALIGN; /* offset for "start" in new mmap block */ our_start -= our_offset; /* file offset for mmap */ we_want = length + MAX_MMAP_CHUNK; if (we_want > we_have) we_want = we_have; we_want += our_offset; if (MAP_FAILED != c->data.file.mmap.data) { munmap(c->data.file.mmap.data, c->data.file.mmap.length); c->data.file.mmap.data = MAP_FAILED; } c->data.file.mmap.offset = our_start; c->data.file.mmap.length = we_want; if (!c->mem) { /* mmap did not fail till now */ c->data.file.mmap.data = mmap(0, we_want, PROT_READ, MAP_SHARED, c->data.file.file->fd, our_start); mmap_errno = errno; } if (MAP_FAILED == c->data.file.mmap.data) { /* fallback to pread(...) */ if (!c->mem) { c->mem = g_byte_array_sized_new(we_want); } else { g_byte_array_set_size(c->mem, we_want); } read_chunk: if (-1 == (we_have = pread(c->data.file.file->fd, c->mem->data, we_want, our_start))) { if (EINTR == errno) goto read_chunk; /* prefer the error of the first syscall */ if (0 != mmap_errno) { VR_ERROR(vr, "mmap failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd, g_strerror(mmap_errno)); } else { VR_ERROR(vr, "pread failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd, g_strerror(errno)); } g_byte_array_free(c->mem, TRUE); c->mem = NULL; return LI_HANDLER_ERROR; } else if (we_have != we_want) { /* may return less than requested bytes due to signals */ /* CON_TRACE(srv, "read return unexpected number of bytes"); */ we_want = we_have; if (length > we_have) length = we_have; c->data.file.mmap.length = we_want; g_byte_array_set_size(c->mem, we_want); } } else { #ifdef HAVE_MADVISE /* don't advise files < 64Kb */ if (c->data.file.mmap.length > (64*1024) && 0 != madvise(c->data.file.mmap.data, c->data.file.mmap.length, MADV_WILLNEED)) { VR_ERROR(vr, "madvise failed for '%s' (fd = %i): %s", GSTR_SAFE_STR(c->data.file.file->name), c->data.file.file->fd, g_strerror(errno)); } #endif } } *data_start = (c->mem ? (char*) c->mem->data : c->data.file.mmap.data) + start + c->offset + c->data.file.start - c->data.file.mmap.offset; *data_len = length; break; case BUFFER_CHUNK: *data_start = (char*) c->data.buffer.buffer->addr + c->data.buffer.offset + c->offset + start; *data_len = length; break; } return LI_HANDLER_GO_ON; }