• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/lighttpd-1.4.39/src/
1
2#include "network_backends.h"
3
4#include "network.h"
5#include "log.h"
6#include "sys-mmap.h"
7
8#include <setjmp.h>
9#include <signal.h>
10#include <unistd.h>
11
12#include <errno.h>
13#include <string.h>
14
15#define MMAP_CHUNK_SIZE (512*1024)
16
17#ifdef HAVE_LIBSMBCLIENT_H
18#include <libsmbclient.h>
19#endif
20
21#define DBE 0
22
23off_t mmap_align_offset(off_t start) {
24	static long pagesize = 0;
25	if (0 == pagesize) {
26		pagesize = sysconf(_SC_PAGESIZE);
27		force_assert(pagesize < MMAP_CHUNK_SIZE);
28	}
29	force_assert(start >= (start % pagesize));
30	return start - (start % pagesize);
31}
32
33#if defined(USE_MMAP)
34
35static volatile int sigbus_jmp_valid;
36static sigjmp_buf sigbus_jmp;
37
38static void sigbus_handler(int sig) {
39	UNUSED(sig);
40	if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1);
41	log_failed_assert(__FILE__, __LINE__, "SIGBUS");
42}
43
44#if 0
45/* read mmap()ed data into local buffer */
46#define LOCAL_BUFFERING 1
47#endif
48
49int network_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
50	chunk* const c = cq->first;
51	off_t offset, toSend, file_end;
52	ssize_t r;
53	size_t mmap_offset, mmap_avail;
54	const char *data;
55
56	force_assert(NULL != c);
57	force_assert(FILE_CHUNK == c->type);
58	force_assert(c->offset >= 0 && c->offset <= c->file.length);
59
60	offset = c->file.start + c->offset;
61	toSend = c->file.length - c->offset;
62	if (toSend > *p_max_bytes) toSend = *p_max_bytes;
63	file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
64
65	if (0 == toSend) {
66		chunkqueue_remove_finished_chunks(cq);
67		return 0;
68	}
69
70	if (0 != network_open_file_chunk(srv, con, cq)) return -1;
71
72	/* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */
73	if (0 != sigsetjmp(sigbus_jmp, 1)) {
74		sigbus_jmp_valid = 0;
75
76		log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:",
77			c->file.name, c->file.fd);
78
79		munmap(c->file.mmap.start, c->file.mmap.length);
80		c->file.mmap.start = MAP_FAILED;
81#ifdef LOCAL_BUFFERING
82		buffer_reset(c->mem);
83#endif
84
85		return -1;
86	}
87
88	signal(SIGBUS, sigbus_handler);
89
90	/* mmap the buffer if offset is outside old mmap area or not mapped at all */
91	if (MAP_FAILED == c->file.mmap.start
92		|| offset < c->file.mmap.offset
93		|| offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
94
95		if (MAP_FAILED != c->file.mmap.start) {
96			munmap(c->file.mmap.start, c->file.mmap.length);
97			c->file.mmap.start = MAP_FAILED;
98		}
99
100		/* Optimizations for the future:
101		 *
102		 * adaptive mem-mapping
103		 *   the problem:
104		 *     we mmap() the whole file. If someone has alot large files and 32bit
105		 *     machine the virtual address area will be unrun and we will have a failing
106		 *     mmap() call.
107		 *   solution:
108		 *     only mmap 16M in one chunk and move the window as soon as we have finished
109		 *     the first 8M
110		 *
111		 * read-ahead buffering
112		 *   the problem:
113		 *     sending out several large files in parallel trashes the read-ahead of the
114		 *     kernel leading to long wait-for-seek times.
115		 *   solutions: (increasing complexity)
116		 *     1. use madvise
117		 *     2. use a internal read-ahead buffer in the chunk-structure
118		 *     3. use non-blocking IO for file-transfers
119		 *   */
120
121		c->file.mmap.offset = mmap_align_offset(offset);
122
123		/* all mmap()ed areas are MMAP_CHUNK_SIZE except the last which might be smaller */
124		c->file.mmap.length = MMAP_CHUNK_SIZE;
125		if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) {
126			c->file.mmap.length = file_end - c->file.mmap.offset;
127		}
128
129		if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) {
130			log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
131				strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
132			return -1;
133		}
134
135#if defined(LOCAL_BUFFERING)
136		sigbus_jmp_valid = 1;
137		buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length);
138		sigbus_jmp_valid = 0;
139#else
140#  if defined(HAVE_MADVISE)
141		/* don't advise files < 64Kb */
142		if (c->file.mmap.length > (64*1024)) {
143			/* darwin 7 is returning EINVAL all the time and I don't know how to
144			 * detect this at runtime.
145			 *
146			 * ignore the return value for now */
147			madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED);
148		}
149#  endif
150#endif
151	}
152
153	force_assert(offset >= c->file.mmap.offset);
154	mmap_offset = offset - c->file.mmap.offset;
155	force_assert(c->file.mmap.length > mmap_offset);
156	mmap_avail = c->file.mmap.length - mmap_offset;
157	if (toSend > (off_t) mmap_avail) toSend = mmap_avail;
158
159#if defined(LOCAL_BUFFERING)
160	data = c->mem->ptr + mmap_offset;
161#else
162	data = c->file.mmap.start + mmap_offset;
163#endif
164
165	sigbus_jmp_valid = 1;
166#if defined(__WIN32)
167	r = send(fd, data, toSend, 0);
168#else /* __WIN32 */
169	r = write(fd, data, toSend);
170#endif /* __WIN32 */
171	sigbus_jmp_valid = 0;
172
173#if defined(__WIN32)
174	if (r < 0) {
175		int lastError = WSAGetLastError();
176		switch (lastError) {
177		case WSAEINTR:
178		case WSAEWOULDBLOCK:
179			break;
180		case WSAECONNRESET:
181		case WSAETIMEDOUT:
182		case WSAECONNABORTED:
183			return -2;
184		default:
185			log_error_write(srv, __FILE__, __LINE__, "sdd",
186				"send failed: ", lastError, fd);
187			return -1;
188		}
189	}
190#else /* __WIN32 */
191	if (r < 0) {
192		switch (errno) {
193		case EAGAIN:
194		case EINTR:
195			break;
196		case EPIPE:
197		case ECONNRESET:
198			return -2;
199		default:
200			log_error_write(srv, __FILE__, __LINE__, "ssd",
201				"write failed:", strerror(errno), fd);
202			return -1;
203		}
204	}
205#endif /* __WIN32 */
206
207	if (r >= 0) {
208		*p_max_bytes -= r;
209		chunkqueue_mark_written(cq, r);
210	}
211
212	return (r > 0 && r == toSend) ? 0 : -3;
213}
214
215#endif /* USE_MMAP */
216
217int network_write_smbfile_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq, off_t *p_max_bytes) {
218
219
220	chunk* const c = cq->first;
221	ssize_t r;
222	off_t offset;
223	off_t toSend;
224	stat_cache_entry *sce = NULL;
225	//off_t buffer_size = 256*1024;
226	off_t buffer_size = 512*1024;
227	char* buff = NULL;
228#ifdef HAVE_LIBSMBCLIENT_H
229
230	force_assert(NULL != c);
231	force_assert(SMB_CHUNK == c->type);
232	force_assert(c->offset >= 0 && c->offset <= c->file.length);
233
234	Cdbg(DBE, "*****************************************");
235
236	offset = c->file.start + c->offset;
237	toSend = ( c->file.length - c->offset > buffer_size) ? buffer_size : c->file.length - c->offset;
238
239	if (toSend > *p_max_bytes) toSend = *p_max_bytes;
240
241	if (0 == toSend) {
242		chunkqueue_remove_finished_chunks(cq);
243		return 0;
244	}
245
246	if (0 != network_open_file_chunk(srv, con, cq)) return -1;
247
248	buff = malloc(buffer_size);
249
250	if(buff==NULL){
251		Cdbg(DBE, "malloc fail");
252		chunkqueue_remove_finished_chunks(cq);
253		return 0;
254	}
255
256	memset(buff, '\0', buffer_size);
257
258	Cdbg(DBE, "size of off_t=%d", sizeof(off_t));
259	Cdbg(DBE, "c->file.fd=[%d], c->file.length=[%lld]", c->file.fd, c->file.length);
260
261	smbc_wrapper_lseek(con, c->file.fd, offset, SEEK_SET );
262
263	Cdbg(DBE, "offset=[%lld], toSend=[%lld]", offset, toSend);
264	r = smbc_wrapper_read(con, c->file.fd, buff, (size_t)toSend);
265
266	Cdbg(DBE, "read complete, r=[%lld]", r);
267
268	if( toSend == -1 || r < 0 ) {
269
270		free(buff);
271
272		smbc_wrapper_close(con, c->file.fd);
273
274		switch (errno) {
275		case EAGAIN:
276		case EINTR:
277			break;
278		case EPIPE:
279		case ECONNRESET:
280			return -2;
281		default:
282			log_error_write(srv, __FILE__, __LINE__, "ssd",
283					"sendfile failed:", strerror(errno), fd);
284			return -1;
285		}
286	}
287	else if (r == 0) {
288
289		free(buff);
290
291		int oerrno = errno;
292		/* We got an event to write but we wrote nothing
293		 *
294		 * - the file shrinked -> error
295		 * - the remote side closed inbetween -> remote-close */
296
297		if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) {
298			/* file is gone ? */
299			return -1;
300		}
301
302		if (offset > sce->st.st_size) {
303			/* file shrinked, close the connection */
304			errno = oerrno;
305
306			return -1;
307		}
308
309		errno = oerrno;
310		return -2;
311	}
312
313	r = write(fd, buff, toSend);
314
315	free(buff);
316
317	if (r >= 0) {
318		chunkqueue_mark_written(cq, r);
319		*p_max_bytes -= r;
320	}
321#endif
322
323	return (r > 0 && r == toSend) ? 0 : -3;
324}
325
326