@@ -885,16 +885,6 @@ void chunkqueue_compact_mem(chunkqueue *cq, size_t clen) {
885
885
}
886
886
887
887
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
-
898
888
if (-1 == c -> file .fd ) {
899
889
/* (permit symlinks; should already have been checked. However, TOC-TOU remains) */
900
890
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
906
896
/*(skip file size checks if file is temp file created by lighttpd)*/
907
897
if (c -> file .is_temp ) return 0 ;
908
898
899
+ force_assert (FILE_CHUNK == c -> type );
900
+ force_assert (c -> offset >= 0 && c -> offset <= c -> file .length );
901
+
902
+ struct stat st ;
909
903
if (-1 == fstat (c -> file .fd , & st )) {
910
904
log_perror (errh , __FILE__ , __LINE__ , "fstat failed" );
911
905
return -1 ;
912
906
}
913
907
908
+ const off_t offset = c -> offset ;
909
+ const off_t toSend = c -> file .length - c -> offset ;
914
910
if (offset > st .st_size || toSend > st .st_size || offset > st .st_size - toSend ) {
915
911
log_error (errh , __FILE__ , __LINE__ , "file shrunk: %s" , c -> mem -> ptr );
916
912
return -1 ;
@@ -924,6 +920,165 @@ int chunkqueue_open_file_chunk(chunkqueue * const restrict cq, log_error_st * co
924
920
}
925
921
926
922
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
+
927
1082
void
928
1083
chunkqueue_small_resp_optim (chunkqueue * const restrict cq )
929
1084
{
@@ -1004,12 +1159,16 @@ chunkqueue_peek_data (chunkqueue * const cq,
1004
1159
toSend = (off_t )space ;
1005
1160
1006
1161
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 );
1008
1164
return -1 ;
1009
1165
}
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 );
1011
1169
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 );
1013
1172
return -1 ;
1014
1173
}
1015
1174
0 commit comments