1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.00 of the Zend license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.zend.com/license/2_00.txt. |
11 | If you did not receive a copy of the Zend license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@zend.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Wez Furlong <wez@thebrainroom.com> |
16 | Scott MacVicar <scottmac@php.net> |
17 | Nuno Lopes <nlopess@php.net> |
18 | Marcus Boerger <helly@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 /* $Id$ */
23
24
25 #include "zend.h"
26 #include "zend_compile.h"
27 #include "zend_stream.h"
28
29 #if HAVE_MMAP
30 # if HAVE_UNISTD_H
31 # include <unistd.h>
32 # if defined(_SC_PAGESIZE)
33 # define REAL_PAGE_SIZE sysconf(_SC_PAGESIZE);
34 # elif defined(_SC_PAGE_SIZE)
35 # define REAL_PAGE_SIZE sysconf(_SC_PAGE_SIZE);
36 # endif
37 # endif
38 # if HAVE_SYS_MMAN_H
39 # include <sys/mman.h>
40 # endif
41 # ifndef REAL_PAGE_SIZE
42 # ifdef PAGE_SIZE
43 # define REAL_PAGE_SIZE PAGE_SIZE
44 # else
45 # define REAL_PAGE_SIZE 4096
46 # endif
47 # endif
48 #endif
49
50 ZEND_DLIMPORT int isatty(int fd);
51
zend_stream_stdio_reader(void * handle,char * buf,size_t len)52 static size_t zend_stream_stdio_reader(void *handle, char *buf, size_t len) /* {{{ */
53 {
54 return fread(buf, 1, len, (FILE*)handle);
55 } /* }}} */
56
zend_stream_stdio_closer(void * handle)57 static void zend_stream_stdio_closer(void *handle) /* {{{ */
58 {
59 if (handle && (FILE*)handle != stdin) {
60 fclose((FILE*)handle);
61 }
62 } /* }}} */
63
zend_stream_stdio_fsizer(void * handle)64 static size_t zend_stream_stdio_fsizer(void *handle) /* {{{ */
65 {
66 zend_stat_t buf;
67 if (handle && zend_fstat(fileno((FILE*)handle), &buf) == 0) {
68 #ifdef S_ISREG
69 if (!S_ISREG(buf.st_mode)) {
70 return 0;
71 }
72 #endif
73 return buf.st_size;
74 }
75 return 0;
76 } /* }}} */
77
zend_stream_unmap(zend_stream * stream)78 static void zend_stream_unmap(zend_stream *stream) { /* {{{ */
79 #if HAVE_MMAP
80 if (stream->mmap.map) {
81 munmap(stream->mmap.map, stream->mmap.len + ZEND_MMAP_AHEAD);
82 } else
83 #endif
84 if (stream->mmap.buf) {
85 efree(stream->mmap.buf);
86 }
87 stream->mmap.len = 0;
88 stream->mmap.pos = 0;
89 stream->mmap.map = 0;
90 stream->mmap.buf = 0;
91 stream->handle = stream->mmap.old_handle;
92 } /* }}} */
93
zend_stream_mmap_closer(zend_stream * stream)94 static void zend_stream_mmap_closer(zend_stream *stream) /* {{{ */
95 {
96 zend_stream_unmap(stream);
97 if (stream->mmap.old_closer && stream->handle) {
98 stream->mmap.old_closer(stream->handle);
99 }
100 } /* }}} */
101
zend_stream_is_mmap(zend_file_handle * file_handle)102 static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */
103 return file_handle->type == ZEND_HANDLE_MAPPED;
104 } /* }}} */
105
zend_stream_fsize(zend_file_handle * file_handle)106 static size_t zend_stream_fsize(zend_file_handle *file_handle) /* {{{ */
107 {
108 zend_stat_t buf;
109
110 if (zend_stream_is_mmap(file_handle)) {
111 return file_handle->handle.stream.mmap.len;
112 }
113 if (file_handle->type == ZEND_HANDLE_STREAM || file_handle->type == ZEND_HANDLE_MAPPED) {
114 return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle);
115 }
116 if (file_handle->handle.fp && zend_fstat(fileno(file_handle->handle.fp), &buf) == 0) {
117 #ifdef S_ISREG
118 if (!S_ISREG(buf.st_mode)) {
119 return 0;
120 }
121 #endif
122 return buf.st_size;
123 }
124
125 return -1;
126 } /* }}} */
127
zend_stream_open(const char * filename,zend_file_handle * handle)128 ZEND_API int zend_stream_open(const char *filename, zend_file_handle *handle) /* {{{ */
129 {
130 if (zend_stream_open_function) {
131 return zend_stream_open_function(filename, handle);
132 }
133 handle->type = ZEND_HANDLE_FP;
134 handle->opened_path = NULL;
135 handle->handle.fp = zend_fopen(filename, &handle->opened_path);
136 handle->filename = filename;
137 handle->free_filename = 0;
138 memset(&handle->handle.stream.mmap, 0, sizeof(zend_mmap));
139
140 return (handle->handle.fp) ? SUCCESS : FAILURE;
141 } /* }}} */
142
zend_stream_getc(zend_file_handle * file_handle)143 static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
144 {
145 char buf;
146
147 if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
148 return (int)buf;
149 }
150 return EOF;
151 } /* }}} */
152
zend_stream_read(zend_file_handle * file_handle,char * buf,size_t len)153 static size_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
154 {
155 if (!zend_stream_is_mmap(file_handle) && file_handle->handle.stream.isatty) {
156 int c = '*';
157 size_t n;
158
159 #ifdef NETWARE
160 /*
161 c != 4 check is there as fread of a character in NetWare LibC gives 4 upon ^D character.
162 Ascii value 4 is actually EOT character which is not defined anywhere in the LibC
163 or else we can use instead of hardcoded 4.
164 */
165 for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != 4 && c != '\n'; ++n) {
166 #else
167 for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n) {
168 #endif
169 buf[n] = (char)c;
170 }
171 if (c == '\n') {
172 buf[n++] = (char)c;
173 }
174
175 return n;
176 }
177 return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
178 } /* }}} */
179
180 ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
181 {
182 size_t size;
183 zend_stream_type old_type;
184
185 if (file_handle->type == ZEND_HANDLE_FILENAME) {
186 if (zend_stream_open(file_handle->filename, file_handle) == FAILURE) {
187 return FAILURE;
188 }
189 }
190
191 switch (file_handle->type) {
192 case ZEND_HANDLE_FD:
193 file_handle->type = ZEND_HANDLE_FP;
194 file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb");
195 /* no break; */
196 case ZEND_HANDLE_FP:
197 if (!file_handle->handle.fp) {
198 return FAILURE;
199 }
200 memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap));
201 file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0;
202 file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
203 file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
204 file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
205 memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
206 /* no break; */
207 case ZEND_HANDLE_STREAM:
208 /* nothing to do */
209 break;
210
211 case ZEND_HANDLE_MAPPED:
212 file_handle->handle.stream.mmap.pos = 0;
213 *buf = file_handle->handle.stream.mmap.buf;
214 *len = file_handle->handle.stream.mmap.len;
215 return SUCCESS;
216
217 default:
218 return FAILURE;
219 }
220
221 size = zend_stream_fsize(file_handle);
222 if (size == (size_t)-1) {
223 return FAILURE;
224 }
225
226 old_type = file_handle->type;
227 file_handle->type = ZEND_HANDLE_STREAM; /* we might still be _FP but we need fsize() work */
228
229 if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) {
230 #if HAVE_MMAP
231 size_t page_size = REAL_PAGE_SIZE;
232
233 if (file_handle->handle.fp &&
234 size != 0 &&
235 ((size - 1) % page_size) <= page_size - ZEND_MMAP_AHEAD) {
236 /* *buf[size] is zeroed automatically by the kernel */
237 *buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0);
238 if (*buf != MAP_FAILED) {
239 zend_long offset = ftell(file_handle->handle.fp);
240 file_handle->handle.stream.mmap.map = *buf;
241
242 if (offset != -1) {
243 *buf += offset;
244 size -= offset;
245 }
246 file_handle->handle.stream.mmap.buf = *buf;
247 file_handle->handle.stream.mmap.len = size;
248
249 goto return_mapped;
250 }
251 }
252 #endif
253 file_handle->handle.stream.mmap.map = 0;
254 file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD);
255 file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size);
256 } else {
257 size_t read, remain = 4*1024;
258 *buf = emalloc(remain);
259 size = 0;
260
261 while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
262 size += read;
263 remain -= read;
264
265 if (remain == 0) {
266 *buf = safe_erealloc(*buf, size, 2, 0);
267 remain = size;
268 }
269 }
270 file_handle->handle.stream.mmap.map = 0;
271 file_handle->handle.stream.mmap.len = size;
272 if (size && remain < ZEND_MMAP_AHEAD) {
273 *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
274 }
275 file_handle->handle.stream.mmap.buf = *buf;
276 }
277
278 if (file_handle->handle.stream.mmap.len == 0) {
279 *buf = erealloc(*buf, ZEND_MMAP_AHEAD);
280 file_handle->handle.stream.mmap.buf = *buf;
281 }
282
283 if (ZEND_MMAP_AHEAD) {
284 memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD);
285 }
286 #if HAVE_MMAP
287 return_mapped:
288 #endif
289 file_handle->type = ZEND_HANDLE_MAPPED;
290 file_handle->handle.stream.mmap.pos = 0;
291 file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle;
292 file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer;
293 file_handle->handle.stream.handle = &file_handle->handle.stream;
294 file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_mmap_closer;
295
296 *buf = file_handle->handle.stream.mmap.buf;
297 *len = file_handle->handle.stream.mmap.len;
298
299 return SUCCESS;
300 } /* }}} */
301
302 ZEND_API void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
303 {
304 switch (fh->type) {
305 case ZEND_HANDLE_FD:
306 /* nothing to do */
307 break;
308 case ZEND_HANDLE_FP:
309 fclose(fh->handle.fp);
310 break;
311 case ZEND_HANDLE_STREAM:
312 case ZEND_HANDLE_MAPPED:
313 if (fh->handle.stream.closer && fh->handle.stream.handle) {
314 fh->handle.stream.closer(fh->handle.stream.handle);
315 }
316 fh->handle.stream.handle = NULL;
317 break;
318 case ZEND_HANDLE_FILENAME:
319 /* We're only supposed to get here when destructing the used_files hash,
320 * which doesn't really contain open files, but references to their names/paths
321 */
322 break;
323 }
324 if (fh->opened_path) {
325 zend_string_release(fh->opened_path);
326 fh->opened_path = NULL;
327 }
328 if (fh->free_filename && fh->filename) {
329 efree((char*)fh->filename);
330 fh->filename = NULL;
331 }
332 }
333 /* }}} */
334
335 ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
336 {
337 if (fh1->type != fh2->type) {
338 return 0;
339 }
340 switch (fh1->type) {
341 case ZEND_HANDLE_FD:
342 return fh1->handle.fd == fh2->handle.fd;
343 case ZEND_HANDLE_FP:
344 return fh1->handle.fp == fh2->handle.fp;
345 case ZEND_HANDLE_STREAM:
346 return fh1->handle.stream.handle == fh2->handle.stream.handle;
347 case ZEND_HANDLE_MAPPED:
348 return (fh1->handle.stream.handle == &fh1->handle.stream &&
349 fh2->handle.stream.handle == &fh2->handle.stream &&
350 fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle)
351 || fh1->handle.stream.handle == fh2->handle.stream.handle;
352 default:
353 return 0;
354 }
355 return 0;
356 } /* }}} */
357