ssize_t do_sendfilev(int sock, char *dir, int nfiles, int chunk_size) { int i; size_t xferred; sfv_list_t *s; struct sendfilevec *vec, *v; s = find_sfv_list(dir); assert(s); if (chunk_size != 0) return (do_sendfilev_chunked(s, sock, chunk_size)); if ((vec = alloca(nfiles * sizeof (struct sendfilevec))) == 0) { perror("calloc"); return (-1); } v = vec; bzero(vec, nfiles * sizeof (struct sendfilevec)); for (i = 0; i < nfiles; i++) { int r = select_file(s); v->sfv_fd = s->flist[r].fd; v->sfv_len = s->flist[r].size; v++; } return (sendfilev(sock, vec, nfiles, &xferred)); }
ssize_t sendfile (int out_fd, int in_fd, off_t *offset, size_t count) { sendfilevec_t sfv; sfv.sfv_fd = in_fd; sfv.sfv_flag = 0; sfv.sfv_off = *offset; sfv.sfv_len = count; size_t xferred = 0; ssize_t res = sendfilev (out_fd, &sfv, 1, &xferred); *offset += xferred; return res; }
/* Now walk the file_list_t list and count entries */ flist = fhead.next; for (fl = fhead.next; fl; fl = fl->next) count++; assert(count); if ((s = calloc(1, sizeof (sfv_list_t))) == NULL) { perror("calloc"); return (-1); } if ((s->flist = calloc(count, sizeof (struct file_list))) == NULL) { perror("calloc"); return (-1); } s->nfiles = count; flist = s->flist; (void) strlcpy(s->dir, dir, sizeof (s->dir)); for (fl = fhead.next; fl; fl = fl->next) { (void) memcpy(flist, fl, sizeof (file_list_t)); flist++; } if (sfv_list) { s->next = sfv_list->next; sfv_list->next = s; } else { sfv_list = s; } return (0); } #ifdef HAVE_SENDFILEV /* Linux does not have sendfilev */ static ssize_t do_sendfilev_chunked(sfv_list_t *s, int sock, int csize) { int size, r, n, xferred; struct sendfilevec vec; r = select_file(s); size = 0; vec.sfv_fd = s->flist[r].fd; vec.sfv_flag = 0; while (size < s->flist[r].size) { vec.sfv_off = size; vec.sfv_len = MIN(csize, s->flist[r].size - size); if ((n = sendfilev(sock, &vec, 1, (size_t *)&xferred)) <= 0) return (n); size += n; } return (size); }
int network_write_chunkqueue_solarissendfilev(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { chunk *c; for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { int chunk_finished = 0; switch(c->type) { case MEM_CHUNK: { char * offset; off_t toSend; ssize_t r; size_t num_chunks, i; struct iovec chunks[UIO_MAXIOV]; chunk *tc; size_t num_bytes = 0; /* we can't send more then SSIZE_MAX bytes in one chunk */ /* build writev list * * 1. limit: num_chunks < UIO_MAXIOV * 2. limit: num_bytes < SSIZE_MAX */ for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { if (tc->mem->used == 0) { chunks[i].iov_base = tc->mem->ptr; chunks[i].iov_len = 0; } else { offset = tc->mem->ptr + tc->offset; toSend = tc->mem->used - 1 - tc->offset; chunks[i].iov_base = offset; /* protect the return value of writev() */ if (toSend > max_bytes || (off_t) num_bytes + toSend > max_bytes) { chunks[i].iov_len = max_bytes - num_bytes; num_chunks = i + 1; break; } else { chunks[i].iov_len = toSend; } num_bytes += toSend; } } if ((r = writev(fd, chunks, num_chunks)) < 0) { switch (errno) { case EAGAIN: case EINTR: r = 0; break; case EPIPE: case ECONNRESET: return -2; default: log_error_write(srv, __FILE__, __LINE__, "ssd", "writev failed:", strerror(errno), fd); return -1; } } /* check which chunks have been written */ cq->bytes_out += r; for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { if (r >= (ssize_t)chunks[i].iov_len) { /* written */ r -= chunks[i].iov_len; tc->offset += chunks[i].iov_len; if (chunk_finished) { /* skip the chunks from further touches */ c = c->next; } else { /* chunks_written + c = c->next is done in the for()*/ chunk_finished = 1; } } else { /* partially written */ tc->offset += r; chunk_finished = 0; break; } } break; } case FILE_CHUNK: { ssize_t r; off_t offset, toSend; size_t written; sendfilevec_t fvec; stat_cache_entry *sce = NULL; int ifd; if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); return -1; } offset = c->file.start + c->offset; toSend = c->file.length - c->offset; if (toSend > max_bytes) toSend = max_bytes; if (offset > sce->st.st_size) { log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); return -1; } if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); return -1; } fvec.sfv_fd = ifd; fvec.sfv_flag = 0; fvec.sfv_off = offset; fvec.sfv_len = toSend; /* Solaris sendfilev() */ if (-1 == (r = sendfilev(fd, &fvec, 1, &written))) { if (errno != EAGAIN) { log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); close(ifd); return -1; } r = 0; } close(ifd); c->offset += written; cq->bytes_out += written; max_bytes -= written; if (c->offset == c->file.length) { chunk_finished = 1; } break; } default: log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); return -1; } if (!chunk_finished) { /* not finished yet */ break; } } return 0; }
ngx_chain_t * ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int fd; u_char *prev; off_t size, send, prev_send, aligned, fprev; size_t sent; ssize_t n; ngx_int_t eintr; ngx_err_t err; ngx_uint_t nsfv; sendfilevec_t *sfv, sfvs[NGX_SENDFILEVECS]; ngx_event_t *wev; ngx_chain_t *cl; wev = c->write; if (!wev->ready) { return in; } if (!c->sendfile) { return ngx_writev_chain(c, in, limit); } /* the maximum limit size is the maximum size_t value - the page size */ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; } send = 0; for ( ;; ) { fd = SFV_FD_SELF; prev = NULL; fprev = 0; sfv = NULL; eintr = 0; sent = 0; prev_send = send; nsfv = 0; /* create the sendfilevec and coalesce the neighbouring bufs */ for (cl = in; cl && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (ngx_buf_in_memory_only(cl->buf)) { fd = SFV_FD_SELF; size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = limit - send; } if (prev == cl->buf->pos) { sfv->sfv_len += (size_t) size; } else { if (nsfv == NGX_SENDFILEVECS) { break; } sfv = &sfvs[nsfv++]; sfv->sfv_fd = SFV_FD_SELF; sfv->sfv_flag = 0; sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos; sfv->sfv_len = (size_t) size; } prev = cl->buf->pos + (size_t) size; send += size; } else { prev = NULL; size = cl->buf->file_last - cl->buf->file_pos; if (send + size > limit) { size = limit - send; aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) & ~((off_t) ngx_pagesize - 1); if (aligned <= cl->buf->file_last) { size = aligned - cl->buf->file_pos; } } if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) { sfv->sfv_len += (size_t) size; } else { if (nsfv == NGX_SENDFILEVECS) { break; } sfv = &sfvs[nsfv++]; fd = cl->buf->file->fd; sfv->sfv_fd = fd; sfv->sfv_flag = 0; sfv->sfv_off = cl->buf->file_pos; sfv->sfv_len = (size_t) size; } fprev = cl->buf->file_pos + size; send += size; } } n = sendfilev(c->fd, sfvs, nsfv, &sent); if (n == -1) { err = ngx_errno; switch (err) { case NGX_EAGAIN: break; case NGX_EINTR: eintr = 1; break; default: wev->error = 1; ngx_connection_error(c, err, "sendfilev() failed"); return NGX_CHAIN_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, "sendfilev() sent only %uz bytes", sent); } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfilev: %z %z", n, sent); c->sent += sent; in = ngx_chain_update_sent(in, sent); if (eintr) { send = prev_send + sent; continue; } if (send - prev_send != (off_t) sent) { wev->ready = 0; return in; } if (send >= limit || in == NULL) { return in; } } }
ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) { int sfvcnt; size_t total, xferred; struct sendfilevec vec[2]; ssize_t hdr_len = 0; if (header) { sfvcnt = 2; vec[0].sfv_fd = SFV_FD_SELF; vec[0].sfv_flag = 0; vec[0].sfv_off = (off_t)header->data; vec[0].sfv_len = hdr_len = header->length; vec[1].sfv_fd = fromfd; vec[1].sfv_flag = 0; vec[1].sfv_off = offset; vec[1].sfv_len = count; } else { sfvcnt = 1; vec[0].sfv_fd = fromfd; vec[0].sfv_flag = 0; vec[0].sfv_off = offset; vec[0].sfv_len = count; } total = count + hdr_len; while (total) { ssize_t nwritten; /* * Although not listed in the API error returns, this is almost certainly * a slow system call and will be interrupted by a signal with EINTR. JRA. */ xferred = 0; #if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64) nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred); #else nwritten = sendfilev(tofd, vec, sfvcnt, &xferred); #endif if (nwritten == -1 && errno == EINTR) { if (xferred == 0) continue; /* Nothing written yet. */ else nwritten = xferred; } if (nwritten == -1) return -1; if (nwritten == 0) return -1; /* I think we're at EOF here... */ /* * If this was a short (signal interrupted) write we may need * to subtract it from the header data, or null out the header * data altogether if we wrote more than vec[0].sfv_len bytes. * We move vec[1].* to vec[0].* and set sfvcnt to 1 */ if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) { vec[1].sfv_off += nwritten - vec[0].sfv_len; vec[1].sfv_len -= nwritten - vec[0].sfv_len; /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */ vec[0] = vec[1]; sfvcnt = 1; } else { vec[0].sfv_off += nwritten; vec[0].sfv_len -= nwritten; } total -= nwritten; } return count + hdr_len; }
ssize_t dsi_stream_read_file(DSI *dsi, const int fromfd, off_t offset, const size_t length, const int err) { int ret = 0; size_t written = 0; size_t total = length; ssize_t len; off_t pos = offset; char block[DSI_BLOCKSIZ]; #ifdef HAVE_SENDFILEV int sfvcnt; struct sendfilevec vec[2]; ssize_t nwritten; #elif defined(FREEBSD) ssize_t nwritten; void *hdrp; struct sf_hdtr hdr; struct iovec iovec; hdr.headers = &iovec; hdr.hdr_cnt = 1; hdr.trailers = NULL; hdr.trl_cnt = 0; hdrp = &hdr; #endif LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file(off: %jd, len: %zu)", (intmax_t)offset, length); if (dsi->flags & DSI_DISCONNECTED) return -1; dsi->in_write++; dsi->flags |= DSI_NOREPLY; dsi->header.dsi_flags = DSIFL_REPLY; dsi->header.dsi_len = htonl(length); dsi->header.dsi_code = htonl(err); dsi_header_pack_reply(dsi, block); #ifdef HAVE_SENDFILEV total += DSI_BLOCKSIZ; sfvcnt = 2; vec[0].sfv_fd = SFV_FD_SELF; vec[0].sfv_flag = 0; /* Cast to unsigned long to prevent sign extension of the * pointer value for the LFS case; see Apache PR 39463. */ vec[0].sfv_off = (unsigned long)block; vec[0].sfv_len = DSI_BLOCKSIZ; vec[1].sfv_fd = fromfd; vec[1].sfv_flag = 0; vec[1].sfv_off = offset; vec[1].sfv_len = length; #elif defined(FREEBSD) iovec.iov_base = block; iovec.iov_len = DSI_BLOCKSIZ; #else dsi_stream_write(dsi, block, sizeof(block), DSI_MSG_MORE); #endif while (written < total) { #ifdef HAVE_SENDFILEV nwritten = 0; len = sendfilev(dsi->socket, vec, sfvcnt, &nwritten); #elif defined(FREEBSD) len = sendfile(fromfd, dsi->socket, pos, total - written, hdrp, &nwritten, 0); if (len == 0) len = nwritten; #else len = sys_sendfile(dsi->socket, fromfd, &pos, total - written); #endif if (len < 0) { switch (errno) { case EINTR: case EAGAIN: len = 0; #if defined(HAVE_SENDFILEV) || defined(FREEBSD) len = (size_t)nwritten; #elif defined(SOLARIS) if (pos > offset) { /* we actually have sent sth., adjust counters and keep trying */ len = pos - offset; offset = pos; } #endif /* HAVE_SENDFILEV */ if (dsi_peek(dsi) != 0) { ret = -1; goto exit; } break; default: LOG(log_error, logtype_dsi, "dsi_stream_read_file: %s", strerror(errno)); ret = -1; goto exit; } } else if (len == 0) { /* afpd is going to exit */ ret = -1; goto exit; } #ifdef HAVE_SENDFILEV if (sfvcnt == 2 && len >= vec[0].sfv_len) { vec[1].sfv_off += len - vec[0].sfv_len; vec[1].sfv_len -= len - vec[0].sfv_len; vec[0] = vec[1]; sfvcnt = 1; } else { vec[0].sfv_off += len; vec[0].sfv_len -= len; } #elif defined(FREEBSD) if (hdrp) { if (len >= iovec.iov_len) { hdrp = NULL; len -= iovec.iov_len; /* len now contains how much sendfile() actually sent from the file */ } else { iovec.iov_len -= len; iovec.iov_base += len; len = 0; } } pos += len; #endif /* HAVE_SENDFILEV */ LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: wrote: %zd", len); written += len; } #ifdef HAVE_SENDFILEV written -= DSI_BLOCKSIZ; #endif dsi->write_count += written; exit: dsi->in_write--; LOG(log_maxdebug, logtype_dsi, "dsi_stream_read_file: written: %zd", written); if (ret != 0) return -1; return written; }
static int do_sendfile(const int out_fd, const int in_fd, unsigned int num_send, filesize_t start_pos) { /* Probably should one day be shared with instance in ftpdataio.c */ static char* p_recvbuf; unsigned int total_written = 0; int retval; enum EVSFSysUtilError error; (void) start_pos; (void) error; #if defined(VSF_SYSDEP_HAVE_LINUX_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_HPUX_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) || \ defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE) if (tunable_use_sendfile) { static int s_sendfile_checked; static int s_runtime_sendfile_works; if (!s_sendfile_checked || s_runtime_sendfile_works) { do { #ifdef VSF_SYSDEP_HAVE_LINUX_SENDFILE retval = sendfile(out_fd, in_fd, NULL, num_send); #elif defined(VSF_SYSDEP_HAVE_FREEBSD_SENDFILE) { /* XXX - start_pos will truncate on 32-bit machines - can we * say "start from current pos"? */ off_t written = 0; retval = sendfile(in_fd, out_fd, start_pos, num_send, NULL, &written, 0); /* Translate to Linux-like retval */ if (written > 0) { retval = (int) written; } } #elif defined(VSF_SYSDEP_HAVE_SOLARIS_SENDFILE) { size_t written = 0; struct sendfilevec the_vec; vsf_sysutil_memclr(&the_vec, sizeof(the_vec)); the_vec.sfv_fd = in_fd; the_vec.sfv_off = start_pos; the_vec.sfv_len = num_send; retval = sendfilev(out_fd, &the_vec, 1, &written); /* Translate to Linux-like retval */ if (written > 0) { retval = (int) written; } } #elif defined(VSF_SYSDEP_HAVE_AIX_SENDFILE) { struct sf_parms sf_iobuf; vsf_sysutil_memclr(&sf_iobuf, sizeof(sf_iobuf)); sf_iobuf.header_data = NULL; sf_iobuf.header_length = 0; sf_iobuf.trailer_data = NULL; sf_iobuf.trailer_length = 0; sf_iobuf.file_descriptor = in_fd; sf_iobuf.file_offset = start_pos; sf_iobuf.file_bytes = num_send; retval = send_file((int*)&out_fd, &sf_iobuf, 0); if (retval >= 0) { retval = sf_iobuf.bytes_sent; } } #else /* must be VSF_SYSDEP_HAVE_HPUX_SENDFILE */ { retval = sendfile(out_fd, in_fd, start_pos, num_send, NULL, 0); } #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE */ error = vsf_sysutil_get_error(); vsf_sysutil_check_pending_actions(kVSFSysUtilIO, retval, out_fd); } while (vsf_sysutil_retval_is_error(retval) && error == kVSFSysUtilErrINTR); if (!s_sendfile_checked) { s_sendfile_checked = 1; if (!vsf_sysutil_retval_is_error(retval) || error != kVSFSysUtilErrNOSYS) { s_runtime_sendfile_works = 1; } } if (!vsf_sysutil_retval_is_error(retval)) { return retval; } if (s_runtime_sendfile_works && error != kVSFSysUtilErrINVAL && error != kVSFSysUtilErrOPNOTSUPP) { return retval; } /* Fall thru to normal implementation. We won't check again. NOTE - * also falls through if sendfile() is OK but it returns EINVAL. For * Linux this means the file was not page cache backed. Original * complaint was trying to serve files from an NTFS filesystem! */ } } #endif /* VSF_SYSDEP_HAVE_LINUX_SENDFILE || VSF_SYSDEP_HAVE_FREEBSD_SENDFILE */ if (p_recvbuf == 0) { vsf_secbuf_alloc(&p_recvbuf, VSFTP_DATA_BUFSIZE); } while (1) { unsigned int num_read; unsigned int num_written; unsigned int num_read_this_time = VSFTP_DATA_BUFSIZE; if (num_read_this_time > num_send) { num_read_this_time = num_send; } retval = vsf_sysutil_read(in_fd, p_recvbuf, num_read_this_time); if (retval < 0) { return retval; } else if (retval == 0) { return -1; } num_read = (unsigned int) retval; retval = vsf_sysutil_write_loop(out_fd, p_recvbuf, num_read); if (retval < 0) { return retval; } num_written = (unsigned int) retval; total_written += num_written; if (num_written != num_read) { return num_written; } if (num_written > num_send) { bug("num_written bigger than num_send in do_sendfile"); } num_send -= num_written; if (num_send == 0) { /* Bingo! */ return total_written; } } }
void WRW_Sendfile(struct worker *w, int fd, off_t off, unsigned len) { struct wrw *wrw; CHECK_OBJ_NOTNULL(w, WORKER_MAGIC); wrw = &w->wrw; AN(wrw->wfd); assert(fd >= 0); assert(len > 0); #if defined(__FreeBSD__) || defined(__DragonFly__) do { struct sf_hdtr sfh; memset(&sfh, 0, sizeof sfh); if (wrw->niov > 0) { sfh.headers = wrw->iov; sfh.hdr_cnt = wrw->niov; } if (sendfile(fd, *wrw->wfd, off, len, &sfh, NULL, 0) != 0) wrw->werr++; wrw->liov = 0; wrw->niov = 0; } while (0); #elif defined(__linux__) do { if (WRK_Flush(w) == 0 && sendfile(*wrw->wfd, fd, &off, len) != len) wrw->werr++; } while (0); #elif defined(__sun) && defined(HAVE_SENDFILEV) do { sendfilevec_t svvec[params->http_headers * 2 + 1]; size_t xferred = 0, expected = 0; int i; for (i = 0; i < wrw->niov; i++) { svvec[i].sfv_fd = SFV_FD_SELF; svvec[i].sfv_flag = 0; svvec[i].sfv_off = (off_t) wrw->iov[i].iov_base; svvec[i].sfv_len = wrw->iov[i].iov_len; expected += svvec[i].sfv_len; } svvec[i].sfv_fd = fd; svvec[i].sfv_flag = 0; svvec[i].sfv_off = off; svvec[i].sfv_len = len; expected += svvec[i].sfv_len; if (sendfilev(*wrw->wfd, svvec, i, &xferred) == -1 || xferred != expected) wrw->werr++; wrw->liov = 0; wrw->niov = 0; } while (0); #elif defined(__sun) && defined(HAVE_SENDFILE) do { if (WRK_Flush(w) == 0 && sendfile(*wrw->wfd, fd, &off, len) != len) wrw->werr++; } while (0); #else #error Unknown sendfile() implementation #endif }
apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, apr_hdtr_t *hdtr, apr_off_t *offset, apr_size_t *len, apr_int32_t flags) { apr_status_t rv, arv; apr_size_t nbytes; sendfilevec_t *sfv; int vecs, curvec, i, repeat; apr_size_t requested_len = 0; if (!hdtr) { hdtr = &no_hdtr; } /* Ignore flags for now. */ flags = 0; /* Calculate how much space we need. */ vecs = hdtr->numheaders + hdtr->numtrailers + 1; sfv = apr_palloc(sock->pool, sizeof(sendfilevec_t) * vecs); curvec = 0; /* Add the headers */ for (i = 0; i < hdtr->numheaders; i++, curvec++) { sfv[curvec].sfv_fd = SFV_FD_SELF; sfv[curvec].sfv_flag = 0; /* Cast to unsigned long to prevent sign extension of the * pointer value for the LFS case; see PR 39463. */ sfv[curvec].sfv_off = (unsigned long)hdtr->headers[i].iov_base; sfv[curvec].sfv_len = hdtr->headers[i].iov_len; requested_len += sfv[curvec].sfv_len; } /* If the len is 0, we skip the file. */ if (*len) { sfv[curvec].sfv_fd = file->filedes; sfv[curvec].sfv_flag = 0; sfv[curvec].sfv_off = *offset; sfv[curvec].sfv_len = *len; requested_len += sfv[curvec].sfv_len; curvec++; } else { vecs--; } /* Add the footers */ for (i = 0; i < hdtr->numtrailers; i++, curvec++) { sfv[curvec].sfv_fd = SFV_FD_SELF; sfv[curvec].sfv_flag = 0; sfv[curvec].sfv_off = (unsigned long)hdtr->trailers[i].iov_base; sfv[curvec].sfv_len = hdtr->trailers[i].iov_len; requested_len += sfv[curvec].sfv_len; } /* If the last write couldn't send all the requested data, * wait for the socket to become writable before proceeding */ if (sock->options & APR_INCOMPLETE_WRITE) { sock->options &= ~APR_INCOMPLETE_WRITE; arv = apr_wait_for_io_or_timeout(NULL, sock, 0); if (arv != APR_SUCCESS) { *len = 0; return arv; } } /* Actually do the sendfilev * * Solaris may return -1/EAGAIN even if it sent bytes on a non-block sock. * * If no bytes were originally sent (nbytes == 0) and we are on a TIMEOUT * socket (which as far as the OS is concerned is a non-blocking socket), * we want to retry after waiting for the other side to read the data (as * determined by poll). Once it is clear to send, we want to retry * sending the sendfilevec_t once more. */ arv = 0; do { /* Clear out the repeat */ repeat = 0; /* socket, vecs, number of vecs, bytes written */ rv = sendfilev(sock->socketdes, sfv, vecs, &nbytes); if (rv == -1 && errno == EAGAIN) { if (nbytes) { rv = 0; } else if (!arv && (sock->timeout > 0)) { apr_status_t t = apr_wait_for_io_or_timeout(NULL, sock, 0); if (t != APR_SUCCESS) { *len = 0; return t; } arv = 1; repeat = 1; } } } while ((rv == -1 && errno == EINTR) || repeat); if (rv == -1) { *len = 0; return errno; } /* Update how much we sent */ *len = nbytes; if ((sock->timeout > 0) && (*len < requested_len)) { sock->options |= APR_INCOMPLETE_WRITE; } return APR_SUCCESS; }
int main(int argc, const char *argv[]) { int test_fd = open(TEST_FILE, O_WRONLY | O_CREAT, 0666); if (test_fd < 0) { int error = errno; fprintf(stderr, "open failed: %s (%d).\n", strerror(error), error); return 1; } char chunk1[CHUNK]; bzero(&chunk1, sizeof(chunk1)); ssize_t nbytes = write(test_fd, &chunk1, sizeof(chunk1)); if (nbytes != CHUNK) { int error = errno; fprintf(stderr, "write failed (nbytes=%zd): %s (%d).\n", nbytes, strerror(error), error); return 1; } close(test_fd); printf("Test file created.\n"); test_fd = open(TEST_FILE, O_RDWR, 0666); if (test_fd < 0) { int error = errno; fprintf(stderr, "open failed: %s (%d).\n", strerror(error), error); return 1; } sendfilevec_t vec[2]; vec[0].sfv_fd = SFV_FD_SELF; vec[0].sfv_off = -1; vec[0].sfv_len = 1; vec[0].sfv_flag = 0; vec[1].sfv_fd = test_fd; vec[1].sfv_off = 0; vec[1].sfv_len = CHUNK; vec[1].sfv_flag = 0; size_t xferred; nbytes = sendfilev(test_fd, vec, 2, &xferred); if (nbytes < 0) { if (errno == EFAULT) { printf("Received EFAULT as expected.\n"); } else { fprintf(stderr, "Expected EFAULT, got %d.\n", errno); } } else { fprintf(stderr, "Error: sendfilev returned a positive value.\n"); } nbytes = sendfilev(test_fd, vec, -1, &xferred); if (nbytes < 0) { if (errno == EINVAL) { printf("Received EINVAL as expected.\n"); } else { fprintf(stderr, "Expected EINVAL, got %d.\n", errno); } } else { fprintf(stderr, "Error: sendfilev returned a positive value.\n"); } vec[0].sfv_off = (off_t) "HEADER"; vec[0].sfv_len = 6; nbytes = sendfilev(test_fd, vec, 1, &xferred); if (nbytes < 0) { int error = errno; fprintf(stderr, "sendfilev failed: %s (%d).\n", strerror(error), error); } else { printf("sendfilev for the first buffer succeeded.\n"); } unlink(TEST_FILE); return 0; }