Skip to content

Commit 2639e5a

Browse files
committed
[multiple] chunkqueue_write_chunk()
create API in chunk.[ch] for writing a chunk to an fd (pull similar code from mod_cgi and mod_webdav) This new API is intended for use on request body input, which is written to size-limited temporary files controlled by lighttpd and written to files or pipes. (network_backend_write() is for writing chunkqueues to sockets)
1 parent 15bfe5e commit 2639e5a

File tree

4 files changed

+208
-272
lines changed

4 files changed

+208
-272
lines changed

src/chunk.c

+172-13
Original file line numberDiff line numberDiff line change
@@ -885,16 +885,6 @@ void chunkqueue_compact_mem(chunkqueue *cq, size_t clen) {
885885
}
886886

887887
static int chunk_open_file_chunk(chunk * const restrict c, log_error_st * const restrict errh) {
888-
off_t offset, toSend;
889-
struct stat st;
890-
891-
force_assert(NULL != c);
892-
force_assert(FILE_CHUNK == c->type);
893-
force_assert(c->offset >= 0 && c->offset <= c->file.length);
894-
895-
offset = c->offset;
896-
toSend = c->file.length - c->offset;
897-
898888
if (-1 == c->file.fd) {
899889
/* (permit symlinks; should already have been checked. However, TOC-TOU remains) */
900890
if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, 1, O_RDONLY, 0))) {
@@ -906,11 +896,17 @@ static int chunk_open_file_chunk(chunk * const restrict c, log_error_st * const
906896
/*(skip file size checks if file is temp file created by lighttpd)*/
907897
if (c->file.is_temp) return 0;
908898

899+
force_assert(FILE_CHUNK == c->type);
900+
force_assert(c->offset >= 0 && c->offset <= c->file.length);
901+
902+
struct stat st;
909903
if (-1 == fstat(c->file.fd, &st)) {
910904
log_perror(errh, __FILE__, __LINE__, "fstat failed");
911905
return -1;
912906
}
913907

908+
const off_t offset = c->offset;
909+
const off_t toSend = c->file.length - c->offset;
914910
if (offset > st.st_size || toSend > st.st_size || offset > st.st_size - toSend) {
915911
log_error(errh, __FILE__, __LINE__, "file shrunk: %s", c->mem->ptr);
916912
return -1;
@@ -924,6 +920,165 @@ int chunkqueue_open_file_chunk(chunkqueue * const restrict cq, log_error_st * co
924920
}
925921

926922

923+
#if defined(HAVE_MMAP)
924+
__attribute_cold__
925+
#endif
926+
__attribute_noinline__
927+
static ssize_t
928+
chunkqueue_write_chunk_file_intermed (const int fd, chunk * const restrict c, log_error_st * const errh)
929+
{
930+
char buf[16384];
931+
char *data = buf;
932+
const off_t count = c->file.length - c->offset;
933+
uint32_t dlen = count < (off_t)sizeof(buf) ? (uint32_t)count : sizeof(buf);
934+
chunkqueue cq = {c,c,0,0,0,0,0}; /*(fake cq for chunkqueue_peek_data())*/
935+
if (0 != chunkqueue_peek_data(&cq, &data, &dlen, errh) && 0 == dlen)
936+
return -1;
937+
ssize_t wr;
938+
do { wr = write(fd, data, dlen); } while (-1 == wr && errno == EINTR);
939+
return wr;
940+
}
941+
942+
943+
#if defined(HAVE_MMAP)
944+
/*(improved from network_write_mmap.c)*/
945+
static off_t
946+
mmap_align_offset (off_t start)
947+
{
948+
static off_t pagemask = 0;
949+
if (0 == pagemask) {
950+
long pagesize = sysconf(_SC_PAGESIZE);
951+
if (-1 == pagesize) pagesize = 4096;
952+
pagemask = ~((off_t)pagesize - 1); /* pagesize always power-of-2 */
953+
}
954+
return (start & pagemask);
955+
}
956+
#endif
957+
958+
959+
#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \
960+
&& (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \
961+
&& defined(__linux__) && !defined HAVE_SENDFILE_BROKEN
962+
#include <sys/sendfile.h>
963+
#include <stdint.h>
964+
#endif
965+
static ssize_t
966+
chunkqueue_write_chunk_file (const int fd, chunk * const restrict c, log_error_st * const errh)
967+
{
968+
/*(similar to network_write_file_chunk_mmap(), but does not use send() on
969+
* Windows because fd is expected to be file or pipe here, not socket)*/
970+
971+
if (0 != chunk_open_file_chunk(c, errh))
972+
return -1;
973+
974+
const off_t count = c->file.length - c->offset;
975+
if (0 == count) return 0; /*(sanity check)*/
976+
977+
ssize_t wr;
978+
#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \
979+
&& (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \
980+
&& defined(__linux__) && !defined HAVE_SENDFILE_BROKEN
981+
/* Linux kernel >= 2.6.33 supports sendfile() between most fd types */
982+
off_t offset = c->offset;
983+
wr = sendfile(fd, c->file.fd, &offset, count<INT32_MAX ? count : INT32_MAX);
984+
if (wr >= 0) return wr;
985+
986+
if (wr < 0 && (errno == EINVAL || errno == ENOSYS))
987+
#endif
988+
{
989+
#if defined(HAVE_MMAP)
990+
/*(caller is responsible for handling SIGBUS if chunkqueue might contain
991+
* untrusted file, i.e. any file other than lighttpd-created tempfile)*/
992+
/*(tempfiles are expected for input, MAP_PRIVATE used for portability)*/
993+
/*(mmaps and writes complete chunk instead of only small parts; files
994+
* are expected to be temp files with reasonable chunk sizes)*/
995+
996+
/* (re)mmap the buffer if range is not covered completely */
997+
if (MAP_FAILED == c->file.mmap.start
998+
|| c->offset < c->file.mmap.offset
999+
|| c->file.length
1000+
> (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
1001+
1002+
if (MAP_FAILED != c->file.mmap.start) {
1003+
munmap(c->file.mmap.start, c->file.mmap.length);
1004+
c->file.mmap.start = MAP_FAILED;
1005+
}
1006+
1007+
c->file.mmap.offset = mmap_align_offset(c->offset);
1008+
c->file.mmap.length = c->file.length - c->file.mmap.offset;
1009+
c->file.mmap.start =
1010+
mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE,
1011+
c->file.fd, c->file.mmap.offset);
1012+
1013+
#if 0
1014+
/* close() fd as soon as fully mmap() rather than when done w/ chunk
1015+
* (possibly worthwhile to keep active fd count lower) */
1016+
if (c->file.is_temp && !c->file.refchg) {
1017+
close(c->file.fd);
1018+
c->file.fd = -1;
1019+
}
1020+
#endif
1021+
}
1022+
1023+
if (MAP_FAILED != c->file.mmap.start) {
1024+
const char * const data =
1025+
c->file.mmap.start + c->offset - c->file.mmap.offset;
1026+
do { wr = write(fd,data,count); } while (-1 == wr && errno==EINTR);
1027+
}
1028+
else
1029+
#endif
1030+
wr = chunkqueue_write_chunk_file_intermed(fd, c, errh);
1031+
}
1032+
return wr;
1033+
}
1034+
1035+
1036+
static ssize_t
1037+
chunkqueue_write_chunk_mem (const int fd, const chunk * const restrict c)
1038+
{
1039+
const void * const buf = c->mem->ptr + c->offset;
1040+
const size_t count = chunk_buffer_string_length(c->mem) - (size_t)c->offset;
1041+
ssize_t wr;
1042+
do { wr = write(fd, buf, count); } while (-1 == wr && errno == EINTR);
1043+
return wr;
1044+
}
1045+
1046+
1047+
ssize_t
1048+
chunkqueue_write_chunk (const int fd, chunkqueue * const restrict cq, log_error_st * const restrict errh)
1049+
{
1050+
/*(note: expects non-empty cq->first)*/
1051+
chunk * const c = cq->first;
1052+
switch (c->type) {
1053+
case MEM_CHUNK:
1054+
return chunkqueue_write_chunk_mem(fd, c);
1055+
case FILE_CHUNK:
1056+
return chunkqueue_write_chunk_file(fd, c, errh);
1057+
default:
1058+
errno = EINVAL;
1059+
return -1;
1060+
}
1061+
}
1062+
1063+
1064+
ssize_t
1065+
chunkqueue_write_chunk_to_pipe (const int fd, chunkqueue * const restrict cq, log_error_st * const restrict errh)
1066+
{
1067+
/*(note: expects non-empty cq->first)*/
1068+
#ifdef SPLICE_F_NONBLOCK /* splice() temp files to pipe on Linux */
1069+
chunk * const c = cq->first;
1070+
if (c->type == FILE_CHUNK) {
1071+
loff_t abs_offset = c->offset;
1072+
return (0 == chunk_open_file_chunk(c, errh))
1073+
? splice(c->file.fd, &abs_offset, fd, NULL,
1074+
(size_t)(c->file.length - c->offset), SPLICE_F_NONBLOCK)
1075+
: -1;
1076+
}
1077+
#endif
1078+
return chunkqueue_write_chunk(fd, cq, errh);
1079+
}
1080+
1081+
9271082
void
9281083
chunkqueue_small_resp_optim (chunkqueue * const restrict cq)
9291084
{
@@ -1004,12 +1159,16 @@ chunkqueue_peek_data (chunkqueue * const cq,
10041159
toSend = (off_t)space;
10051160

10061161
if (-1 == lseek(c->file.fd, offset, SEEK_SET)) {
1007-
log_perror(errh, __FILE__, __LINE__, "lseek");
1162+
log_perror(errh, __FILE__, __LINE__, "lseek(\"%s\")",
1163+
c->mem->ptr);
10081164
return -1;
10091165
}
1010-
toSend = read(c->file.fd, data_in + *dlen, (size_t)toSend);
1166+
do {
1167+
toSend = read(c->file.fd, data_in + *dlen, (size_t)toSend);
1168+
} while (-1 == toSend && errno == EINTR);
10111169
if (toSend <= 0) { /* -1 error; 0 EOF (unexpected) */
1012-
log_perror(errh, __FILE__, __LINE__, "read");
1170+
log_perror(errh, __FILE__, __LINE__, "read(\"%s\")",
1171+
c->mem->ptr);
10131172
return -1;
10141173
}
10151174

src/chunk.h

+3
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ void chunkqueue_compact_mem(chunkqueue *cq, size_t clen);
127127

128128
void chunkqueue_small_resp_optim (chunkqueue * restrict cq);
129129

130+
ssize_t chunkqueue_write_chunk (int fd, chunkqueue * restrict cq, struct log_error_st * restrict errh);
131+
ssize_t chunkqueue_write_chunk_to_pipe (int fd, chunkqueue * restrict cq, struct log_error_st * restrict errh);
132+
130133
int chunkqueue_peek_data (chunkqueue *cq, char **data, uint32_t *dlen, struct log_error_st * restrict errh);
131134
int chunkqueue_read_data (chunkqueue *cq, char *data, uint32_t dlen, struct log_error_st * restrict errh);
132135

0 commit comments

Comments
 (0)