xref: /PHP-7.3/Zend/zend_stream.c (revision 8d3f8ca1)
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 #include "zend.h"
23 #include "zend_compile.h"
24 #include "zend_stream.h"
25 
26 #if HAVE_MMAP
27 # if HAVE_UNISTD_H
28 #  include <unistd.h>
29 #  if defined(_SC_PAGESIZE)
30 #    define REAL_PAGE_SIZE sysconf(_SC_PAGESIZE);
31 #  elif defined(_SC_PAGE_SIZE)
32 #    define REAL_PAGE_SIZE sysconf(_SC_PAGE_SIZE);
33 #  endif
34 # endif
35 # if HAVE_SYS_MMAN_H
36 #  include <sys/mman.h>
37 # endif
38 # ifndef REAL_PAGE_SIZE
39 #  ifdef PAGE_SIZE
40 #   define REAL_PAGE_SIZE PAGE_SIZE
41 #  else
42 #   define REAL_PAGE_SIZE 4096
43 #  endif
44 # endif
45 #endif
46 
47 ZEND_DLIMPORT int isatty(int fd);
48 
zend_stream_stdio_reader(void * handle,char * buf,size_t len)49 static size_t zend_stream_stdio_reader(void *handle, char *buf, size_t len) /* {{{ */
50 {
51 	return fread(buf, 1, len, (FILE*)handle);
52 } /* }}} */
53 
zend_stream_stdio_closer(void * handle)54 static void zend_stream_stdio_closer(void *handle) /* {{{ */
55 {
56 	if (handle && (FILE*)handle != stdin) {
57 		fclose((FILE*)handle);
58 	}
59 } /* }}} */
60 
zend_stream_stdio_fsizer(void * handle)61 static size_t zend_stream_stdio_fsizer(void *handle) /* {{{ */
62 {
63 	zend_stat_t buf;
64 	if (handle && zend_fstat(fileno((FILE*)handle), &buf) == 0) {
65 #ifdef S_ISREG
66 		if (!S_ISREG(buf.st_mode)) {
67 			return 0;
68 		}
69 #endif
70 		return buf.st_size;
71 	}
72 	return 0;
73 } /* }}} */
74 
zend_stream_unmap(zend_stream * stream)75 static void zend_stream_unmap(zend_stream *stream) { /* {{{ */
76 #if HAVE_MMAP
77 	if (stream->mmap.map) {
78 		munmap(stream->mmap.map, stream->mmap.len + ZEND_MMAP_AHEAD);
79 	} else
80 #endif
81 	if (stream->mmap.buf) {
82 		efree(stream->mmap.buf);
83 	}
84 	stream->mmap.len = 0;
85 	stream->mmap.pos = 0;
86 	stream->mmap.map = 0;
87 	stream->mmap.buf = 0;
88 	stream->handle   = stream->mmap.old_handle;
89 } /* }}} */
90 
zend_stream_mmap_closer(zend_stream * stream)91 static void zend_stream_mmap_closer(zend_stream *stream) /* {{{ */
92 {
93 	zend_stream_unmap(stream);
94 	if (stream->mmap.old_closer && stream->handle) {
95 		stream->mmap.old_closer(stream->handle);
96 	}
97 } /* }}} */
98 
zend_stream_is_mmap(zend_file_handle * file_handle)99 static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */
100 	return file_handle->type == ZEND_HANDLE_MAPPED;
101 } /* }}} */
102 
zend_stream_fsize(zend_file_handle * file_handle)103 static size_t zend_stream_fsize(zend_file_handle *file_handle) /* {{{ */
104 {
105 	zend_stat_t buf;
106 
107 	if (zend_stream_is_mmap(file_handle)) {
108 		return file_handle->handle.stream.mmap.len;
109 	}
110 	if (file_handle->type == ZEND_HANDLE_STREAM || file_handle->type == ZEND_HANDLE_MAPPED) {
111 		return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle);
112 	}
113 	if (file_handle->handle.fp && zend_fstat(fileno(file_handle->handle.fp), &buf) == 0) {
114 #ifdef S_ISREG
115 		if (!S_ISREG(buf.st_mode)) {
116 			return 0;
117 		}
118 #endif
119 		return buf.st_size;
120 	}
121 
122 	return -1;
123 } /* }}} */
124 
zend_stream_open(const char * filename,zend_file_handle * handle)125 ZEND_API int zend_stream_open(const char *filename, zend_file_handle *handle) /* {{{ */
126 {
127 	if (zend_stream_open_function) {
128 		return zend_stream_open_function(filename, handle);
129 	}
130 	handle->type = ZEND_HANDLE_FP;
131 	handle->opened_path = NULL;
132 	handle->handle.fp = zend_fopen(filename, &handle->opened_path);
133 	handle->filename = filename;
134 	handle->free_filename = 0;
135 	memset(&handle->handle.stream.mmap, 0, sizeof(zend_mmap));
136 
137 	return (handle->handle.fp) ? SUCCESS : FAILURE;
138 } /* }}} */
139 
zend_stream_getc(zend_file_handle * file_handle)140 static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
141 {
142 	char buf;
143 
144 	if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
145 		return (int)buf;
146 	}
147 	return EOF;
148 } /* }}} */
149 
zend_stream_read(zend_file_handle * file_handle,char * buf,size_t len)150 static size_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
151 {
152 	if (!zend_stream_is_mmap(file_handle) && file_handle->handle.stream.isatty) {
153 		int c = '*';
154 		size_t n;
155 
156 		for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n)  {
157 			buf[n] = (char)c;
158 		}
159 		if (c == '\n') {
160 			buf[n++] = (char)c;
161 		}
162 
163 		return n;
164 	}
165 	return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
166 } /* }}} */
167 
zend_stream_fixup(zend_file_handle * file_handle,char ** buf,size_t * len)168 ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
169 {
170 	size_t size;
171 	zend_stream_type old_type;
172 
173 	if (file_handle->type == ZEND_HANDLE_FILENAME) {
174 		if (zend_stream_open(file_handle->filename, file_handle) == FAILURE) {
175 			return FAILURE;
176 		}
177 	}
178 
179 	switch (file_handle->type) {
180 		case ZEND_HANDLE_FD:
181 			file_handle->type = ZEND_HANDLE_FP;
182 			file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb");
183 			/* no break; */
184 		case ZEND_HANDLE_FP:
185 			if (!file_handle->handle.fp) {
186 				return FAILURE;
187 			}
188 			memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap));
189 			file_handle->handle.stream.isatty     = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0;
190 			file_handle->handle.stream.reader     = (zend_stream_reader_t)zend_stream_stdio_reader;
191 			file_handle->handle.stream.closer     = (zend_stream_closer_t)zend_stream_stdio_closer;
192 			file_handle->handle.stream.fsizer     = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
193 			memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
194 			/* no break; */
195 		case ZEND_HANDLE_STREAM:
196 			/* nothing to do */
197 			break;
198 
199 		case ZEND_HANDLE_MAPPED:
200 			file_handle->handle.stream.mmap.pos = 0;
201 			*buf = file_handle->handle.stream.mmap.buf;
202 			*len = file_handle->handle.stream.mmap.len;
203 			return SUCCESS;
204 
205 		default:
206 			return FAILURE;
207 	}
208 
209 	size = zend_stream_fsize(file_handle);
210 	if (size == (size_t)-1) {
211 		return FAILURE;
212 	}
213 
214 	old_type = file_handle->type;
215 	file_handle->type = ZEND_HANDLE_STREAM;  /* we might still be _FP but we need fsize() work */
216 
217 	if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) {
218 #if HAVE_MMAP
219 		size_t page_size = REAL_PAGE_SIZE;
220 
221 		if (file_handle->handle.fp &&
222 		    size != 0 &&
223 		    ((size - 1) % page_size) <= page_size - ZEND_MMAP_AHEAD) {
224 			/*  *buf[size] is zeroed automatically by the kernel */
225 			*buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0);
226 			if (*buf != MAP_FAILED) {
227 				zend_long offset = ftell(file_handle->handle.fp);
228 				file_handle->handle.stream.mmap.map = *buf;
229 
230 				if (offset != -1) {
231 					*buf += offset;
232 					size -= offset;
233 				}
234 				file_handle->handle.stream.mmap.buf = *buf;
235 				file_handle->handle.stream.mmap.len = size;
236 
237 				goto return_mapped;
238 			}
239 		}
240 #endif
241 		file_handle->handle.stream.mmap.map = 0;
242 		file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD);
243 		file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size);
244 	} else {
245 		size_t read, remain = 4*1024;
246 		*buf = emalloc(remain);
247 		size = 0;
248 
249 		while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
250 			size   += read;
251 			remain -= read;
252 
253 			if (remain == 0) {
254 				*buf   = safe_erealloc(*buf, size, 2, 0);
255 				remain = size;
256 			}
257 		}
258 		file_handle->handle.stream.mmap.map = 0;
259 		file_handle->handle.stream.mmap.len = size;
260 		if (size && remain < ZEND_MMAP_AHEAD) {
261 			*buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
262 		}
263 		file_handle->handle.stream.mmap.buf = *buf;
264 	}
265 
266 	if (file_handle->handle.stream.mmap.len == 0) {
267 		*buf = erealloc(*buf, ZEND_MMAP_AHEAD);
268 		file_handle->handle.stream.mmap.buf = *buf;
269 	}
270 
271 	if (ZEND_MMAP_AHEAD) {
272 		memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD);
273 	}
274 #if HAVE_MMAP
275 return_mapped:
276 #endif
277 	file_handle->type = ZEND_HANDLE_MAPPED;
278 	file_handle->handle.stream.mmap.pos        = 0;
279 	file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle;
280 	file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer;
281 	file_handle->handle.stream.handle          = &file_handle->handle.stream;
282 	file_handle->handle.stream.closer          = (zend_stream_closer_t)zend_stream_mmap_closer;
283 
284 	*buf = file_handle->handle.stream.mmap.buf;
285 	*len = file_handle->handle.stream.mmap.len;
286 
287 	return SUCCESS;
288 } /* }}} */
289 
zend_file_handle_dtor(zend_file_handle * fh)290 ZEND_API void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
291 {
292 	switch (fh->type) {
293 		case ZEND_HANDLE_FD:
294 			/* nothing to do */
295 			break;
296 		case ZEND_HANDLE_FP:
297 			fclose(fh->handle.fp);
298 			break;
299 		case ZEND_HANDLE_STREAM:
300 		case ZEND_HANDLE_MAPPED:
301 			if (fh->handle.stream.closer && fh->handle.stream.handle) {
302 				fh->handle.stream.closer(fh->handle.stream.handle);
303 			}
304 			fh->handle.stream.handle = NULL;
305 			break;
306 		case ZEND_HANDLE_FILENAME:
307 			/* We're only supposed to get here when destructing the used_files hash,
308 			 * which doesn't really contain open files, but references to their names/paths
309 			 */
310 			break;
311 	}
312 	if (fh->opened_path) {
313 		zend_string_release_ex(fh->opened_path, 0);
314 		fh->opened_path = NULL;
315 	}
316 	if (fh->free_filename && fh->filename) {
317 		efree((char*)fh->filename);
318 		fh->filename = NULL;
319 	}
320 }
321 /* }}} */
322 
zend_compare_file_handles(zend_file_handle * fh1,zend_file_handle * fh2)323 ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
324 {
325 	if (fh1->type != fh2->type) {
326 		return 0;
327 	}
328 	switch (fh1->type) {
329 		case ZEND_HANDLE_FD:
330 			return fh1->handle.fd == fh2->handle.fd;
331 		case ZEND_HANDLE_FP:
332 			return fh1->handle.fp == fh2->handle.fp;
333 		case ZEND_HANDLE_STREAM:
334 			return fh1->handle.stream.handle == fh2->handle.stream.handle;
335 		case ZEND_HANDLE_MAPPED:
336 			return (fh1->handle.stream.handle == &fh1->handle.stream &&
337 			        fh2->handle.stream.handle == &fh2->handle.stream &&
338 			        fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle)
339 				|| fh1->handle.stream.handle == fh2->handle.stream.handle;
340 		default:
341 			return 0;
342 	}
343 	return 0;
344 } /* }}} */
345 
346 /*
347  * Local variables:
348  * tab-width: 4
349  * c-basic-offset: 4
350  * indent-tabs-mode: t
351  * End:
352  * vim600: sw=4 ts=4 fdm=marker
353  * vim<600: sw=4 ts=4
354  */
355