Beispiel #1
0
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));
}
Beispiel #2
0
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;
}
Beispiel #3
0
	/* 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);
}
Beispiel #4
0
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;
        }
    }
}
Beispiel #6
0
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;
}
Beispiel #7
0
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;
}
Beispiel #8
0
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;
    }
  }
}
Beispiel #9
0
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
}
Beispiel #10
0
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;
}