/* * kissdx - KiSS PC-Link Daemon eXtended (based on kissd) * * This file Copyright (C) 2006 Vidar Tysse * This file is Public domain. * */ #include #include #include #include #include #include "kissdx.h" #include "sendfile.h" #define bufsize 131072 // 128 KB #ifdef USE_INTERNAL_SENDFILE ssize_t my_internal_sendfile(int dst, int src, off_t *offset, size_t size) { ssize_t len = 0; #ifdef USE_INTERNAL_SENDFILE_MMAP void *sp; off_t paged_offset; size_t mapped_size, mapped_offset; if (size > 0) { // Calculate offset as a multiple of the page size size_t pagesize = 65536; //getpagesize() paged_offset = (*offset / pagesize) * pagesize; mapped_offset = *offset - paged_offset; mapped_size = size + mapped_offset; logv("my_internal_sendfile(%i,%i,%lld,%u): Mapping %u bytes at %lld, to read %u bytes at +%u", dst, src, *offset, size, mapped_size, paged_offset, size, mapped_offset); // Map from paged offset to end of chunk if ((MAP_FAILED != (sp = mmap(NULL, mapped_size, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, src, paged_offset)))) { len = write(dst, sp + mapped_offset, size); logv("my_internal_sendfile(%i,%i,%lld,%u): Sent %u bytes at memloc %p to %u (tried to send %u bytes)", dst, src, *offset, size, len, sp + mapped_offset, dst, size); *offset += len; if (munmap(sp, mapped_size) != 0) log("munmap() failed: %s", strerror(errno)); } else { log("mmap(NULL,%u,%d,%d,%d,%lld) failed: %s", mapped_size, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, src, paged_offset, strerror(errno)); len = -1; } } #else ssize_t read_chunk_len, write_chunk_len, remaining, written; if (size > 0) { // Repeat until requested size has been sent: Seek to file position, read chunk into static buffer and send chunk from buffer static char buf[bufsize]; for (remaining = size; remaining > 0; remaining -= read_chunk_len) { if ((read_chunk_len = pread(src, buf, remaining > bufsize? bufsize : remaining, *offset)) > 0) { // Ensure successful write by looping until the entire read chunk has been written and by repeating interrupted writes. for (written = 0; written < read_chunk_len; written += write_chunk_len) { do { if ((write_chunk_len = write(dst, buf, read_chunk_len)) < 0) { if (errno != EINTR) { log("my_internal_sendfile: write failed: %s", strerror(errno)); return -1; } } } while (write_chunk_len < 0 && errno == EINTR); // Repeat if interrupted } len += read_chunk_len; *offset += read_chunk_len; } else { if (read_chunk_len < 0) { log("my_internal_sendfile: pread() failed: %s", strerror(errno)); return -1; } break; } } } #endif return len; } #endif