version 1.1.1.2, 2014/06/15 20:20:06
|
version 1.1.1.3, 2016/11/02 10:35:00
|
Line 1
|
Line 1
|
|
#include "first.h" |
|
|
#include "network_backends.h" |
#include "network_backends.h" |
|
|
#ifdef USE_FREEBSD_SENDFILE | #if defined(USE_FREEBSD_SENDFILE) |
|
|
#include "network.h" |
#include "network.h" |
#include "fdevent.h" |
|
#include "log.h" |
#include "log.h" |
#include "stat_cache.h" |
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/stat.h> | #include <sys/uio.h> |
#include <sys/time.h> | |
#include <sys/resource.h> | |
|
|
#include <netinet/in.h> |
|
#include <netinet/tcp.h> |
|
|
|
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <netdb.h> |
|
#include <string.h> |
#include <string.h> |
#include <stdlib.h> |
|
|
|
|
int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) { |
|
chunk* const c = cq->first; |
|
off_t offset, written = 0; |
|
off_t toSend; |
|
int r; |
|
|
#ifndef UIO_MAXIOV | force_assert(NULL != c); |
# if defined(__FreeBSD__) || defined(__DragonFly__) | force_assert(FILE_CHUNK == c->type); |
/* FreeBSD 4.7, 4.9 defined it in sys/uio.h only if _KERNEL is specified */ | force_assert(c->offset >= 0 && c->offset <= c->file.length); |
# define UIO_MAXIOV 1024 | |
# endif | |
#endif | |
|
|
int network_write_chunkqueue_freebsdsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) { | offset = c->file.start + c->offset; |
chunk *c; | toSend = c->file.length - c->offset; |
| if (toSend > *p_max_bytes) toSend = *p_max_bytes; |
|
|
for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) { | if (0 == toSend) { |
int chunk_finished = 0; | chunkqueue_remove_finished_chunks(cq); |
| return 0; |
| } |
|
|
switch(c->type) { | if (0 != network_open_file_chunk(srv, con, cq)) return -1; |
case MEM_CHUNK: { | |
char * offset; | |
off_t toSend; | |
ssize_t r; | |
|
|
size_t num_chunks, i; | /* FreeBSD sendfile() */ |
struct iovec chunks[UIO_MAXIOV]; | if (-1 == (r = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0))) { |
chunk *tc; | switch(errno) { |
size_t num_bytes = 0; | case EAGAIN: |
| case EINTR: |
/* build writev list | /* for EAGAIN/EINTR written still contains the sent bytes */ |
* | break; /* try again later */ |
* 1. limit: num_chunks < UIO_MAXIOV | case EPIPE: |
* 2. limit: num_bytes < max_bytes | case ENOTCONN: |
*/ | return -2; |
for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); | case EINVAL: |
| case ENOSYS: |
for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { | #if defined(ENOTSUP) \ |
if (tc->mem->used == 0) { | && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) |
chunks[i].iov_base = tc->mem->ptr; | case ENOTSUP: |
chunks[i].iov_len = 0; | #endif |
} else { | #ifdef EOPNOTSUPP |
offset = tc->mem->ptr + tc->offset; | case EOPNOTSUPP: |
toSend = tc->mem->used - 1 - tc->offset; | #endif |
| #ifdef ESOCKTNOSUPPORT |
chunks[i].iov_base = offset; | case ESOCKTNOSUPPORT: |
| #endif |
/* protect the return value of writev() */ | #ifdef EAFNOSUPPORT |
if (toSend > max_bytes || | case EAFNOSUPPORT: |
(off_t) num_bytes + toSend > max_bytes) { | #endif |
chunks[i].iov_len = max_bytes - num_bytes; | #ifdef USE_MMAP |
| return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes); |
num_chunks = i + 1; | #else |
break; | return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes); |
} else { | #endif |
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 ENOTCONN: | |
case EPIPE: | |
case ECONNRESET: | |
return -2; | |
default: | |
log_error_write(srv, __FILE__, __LINE__, "ssd", | |
"writev failed:", strerror(errno), fd); | |
| |
return -1; | |
} | |
| |
r = 0; | |
} | |
| |
/* check which chunks have been written */ | |
cq->bytes_out += r; | |
max_bytes -= 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: { | |
off_t offset, r; | |
off_t toSend; | |
stat_cache_entry *sce = NULL; | |
| |
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 (-1 == c->file.fd) { | |
if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { | |
log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); | |
| |
return -1; | |
} | |
| |
fd_close_on_exec(c->file.fd); | |
} | |
| |
r = 0; | |
| |
/* FreeBSD sendfile() */ | |
if (-1 == sendfile(c->file.fd, fd, offset, toSend, NULL, &r, 0)) { | |
switch(errno) { | |
case EAGAIN: | |
case EINTR: | |
/* for EAGAIN/EINTR r still contains the sent bytes */ | |
break; /* try again later */ | |
case EPIPE: | |
case ENOTCONN: | |
return -2; | |
default: | |
log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); | |
return -1; | |
} | |
} else if (r == 0) { | |
/* We got an event to write but we wrote nothing | |
* | |
* - the file shrinked -> error | |
* - the remote side closed inbetween -> remote-close */ | |
| |
if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { | |
/* file is gone ? */ | |
return -1; | |
} | |
| |
if (offset >= sce->st.st_size) { | |
/* file shrinked, close the connection */ | |
return -1; | |
} | |
| |
return -2; | |
} | |
| |
c->offset += r; | |
cq->bytes_out += r; | |
max_bytes -= r; | |
| |
if (c->offset == c->file.length) { | |
chunk_finished = 1; | |
} | |
| |
break; | |
} | |
default: |
default: |
| log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); |
log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); | |
| |
return -1; |
return -1; |
} |
} |
|
} |
|
|
if (!chunk_finished) { | if (written >= 0) { |
/* not finished yet */ | chunkqueue_mark_written(cq, written); |
| *p_max_bytes -= written; |
break; | |
} | |
} |
} |
|
|
return 0; | return (r >= 0 && written == toSend) ? 0 : -3; |
} |
} |
|
|
#endif | #endif /* USE_FREEBSD_SENDFILE */ |