Diff for /embedaddon/lighttpd/src/network_linux_sendfile.c between versions 1.1.1.2 and 1.1.1.3

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_LINUX_SENDFILE#if defined(USE_LINUX_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/sendfile.h>
#include <sys/socket.h> 
#include <sys/stat.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>  
 #include <fcntl.h>  
   
/* on linux 2.4.29 + debian/ubuntu we have crashes if this is enabled */int network_write_file_chunk_sendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
#undef HAVE_POSIX_FADVISE        chunk* const c = cq->first;
         ssize_t r;
         off_t offset;
         off_t toSend;
   
int network_write_chunkqueue_linuxsendfile(server *srv, connection *con, int fd, chunkqueue *cq, off_t max_bytes) {        force_assert(NULL != c);
        chunk *c;        force_assert(FILE_CHUNK == c->type);
         force_assert(c->offset >= 0 && c->offset <= c->file.length);
   
        for(c = cq->first; (max_bytes > 0) && (NULL != c); c = c->next) {        offset = c->file.start + c->offset;
                int chunk_finished = 0;        toSend = c->file.length - c->offset;
         if (toSend > *p_max_bytes) toSend = *p_max_bytes;
   
                switch(c->type) {        if (0 == toSend) {
                case MEM_CHUNK: {                chunkqueue_remove_finished_chunks(cq);
                        char * offset;                return 0;
                        off_t toSend;        }
                        ssize_t r; 
   
                        size_t num_chunks, i;        if (0 != network_open_file_chunk(srv, con, cq)) return -1;
                        struct iovec chunks[UIO_MAXIOV]; 
                        chunk *tc; 
                        size_t num_bytes = 0; 
   
                        /* build writev list        if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) {
                         *                switch (errno) {
                         * 1. limit: num_chunks < UIO_MAXIOV                case EAGAIN:
                         * 2. limit: num_bytes < max_bytes                case EINTR:
                         */ 
                        for (num_chunks = 0, tc = c; 
                             tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; 
                             tc = tc->next, num_chunks++); 
 
                        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; 
                        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;                          break;
                }                case EPIPE:
                case FILE_CHUNK: {                case ECONNRESET:
                        ssize_t r;                        return -2;
                        off_t offset;                case EINVAL:
                        off_t toSend;                case ENOSYS:
                        stat_cache_entry *sce = NULL;              #if defined(ENOTSUP) \
                && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP)
                        offset = c->file.start + c->offset;                case ENOTSUP:
                        toSend = c->file.length - c->offset;              #endif
                        if (toSend > max_bytes) toSend = max_bytes;              #ifdef EOPNOTSUPP
                case EOPNOTSUPP:
                        /* open file if not already opened */              #endif
                        if (-1 == c->file.fd) {              #ifdef ESOCKTNOSUPPORT
                                if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {                case ESOCKTNOSUPPORT:
                                        log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));              #endif
              #ifdef EAFNOSUPPORT
                                        return -1;                case EAFNOSUPPORT:
                                }              #endif
                                fd_close_on_exec(c->file.fd);                      #ifdef USE_MMAP
#ifdef HAVE_POSIX_FADVISE                        return network_write_file_chunk_mmap(srv, con, fd, cq, p_max_bytes);
                                /* tell the kernel that we want to stream the file */                      #else
                                if (-1 == posix_fadvise(c->file.fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {                        return network_write_file_chunk_no_mmap(srv, con, fd, cq, p_max_bytes);
                                        if (ENOSYS != errno) {                      #endif
                                                log_error_write(srv, __FILE__, __LINE__, "ssd", 
                                                        "posix_fadvise failed:", strerror(errno), c->file.fd); 
                                        } 
                                } 
#endif 
                        } 
 
                        if (-1 == (r = sendfile(fd, c->file.fd, &offset, toSend))) { 
                                switch (errno) { 
                                case EAGAIN: 
                                case EINTR: 
                                        /* ok, we can't send more, let's try later again */ 
                                        r = 0; 
                                        break; 
                                case EPIPE: 
                                case ECONNRESET: 
                                        return -2; 
                                default: 
                                        log_error_write(srv, __FILE__, __LINE__, "ssd", 
                                                        "sendfile failed:", strerror(errno), fd); 
                                        return -1; 
                                } 
                        } else if (r == 0) { 
                                int oerrno = errno; 
                                /* 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 */ 
                                        errno = oerrno; 
 
                                        return -1; 
                                } 
 
                                errno = oerrno; 
                                return -2; 
                        } 
 
#ifdef HAVE_POSIX_FADVISE 
#if 0 
#define K * 1024 
#define M * 1024 K 
#define READ_AHEAD 4 M 
                        /* check if we need a new chunk */ 
                        if ((c->offset & ~(READ_AHEAD - 1)) != ((c->offset + r) & ~(READ_AHEAD - 1))) { 
                                /* tell the kernel that we want to stream the file */ 
                                if (-1 == posix_fadvise(c->file.fd, (c->offset + r) & ~(READ_AHEAD - 1), READ_AHEAD, POSIX_FADV_NOREUSE)) { 
                                        log_error_write(srv, __FILE__, __LINE__, "ssd", 
                                                "posix_fadvise failed:", strerror(errno), c->file.fd); 
                                } 
                        } 
#endif 
#endif 
 
                        c->offset += r; 
                        cq->bytes_out += r; 
                        max_bytes -= r; 
 
                        if (c->offset == c->file.length) { 
                                chunk_finished = 1; 
 
                                /* chunk_free() / chunk_reset() will cleanup for us but it is a ok to be faster :) */ 
 
                                if (c->file.fd != -1) { 
                                        close(c->file.fd); 
                                        c->file.fd = -1; 
                                } 
                        } 
 
                        break; 
                } 
                 default:                  default:
                        log_error_write(srv, __FILE__, __LINE__, "ssd",
                        log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known");                                        "sendfile failed:", strerror(errno), fd);
 
                         return -1;                          return -1;
                 }                  }
           }
   
                if (!chunk_finished) {        if (r >= 0) {
                        /* not finished yet */                chunkqueue_mark_written(cq, r);
                *p_max_bytes -= r;
                        break; 
                } 
         }          }
   
        return 0;        return (r > 0 && r == toSend) ? 0 : -3;
 }  }
   
#endif#endif /* USE_LINUX_SENDFILE */
#if 0 
network_linuxsendfile_init(void) { 
        p->write = network_linuxsendfile_write_chunkset; 
} 
#endif 

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.3


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