xref: /PHP-7.2/Zend/zend_stream.c (revision 7a7ec01a)
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 		for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n)  {
160 			buf[n] = (char)c;
161 		}
162 		if (c == '\n') {
163 			buf[n++] = (char)c;
164 		}
165 
166 		return n;
167 	}
168 	return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
169 } /* }}} */
170 
zend_stream_fixup(zend_file_handle * file_handle,char ** buf,size_t * len)171 ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
172 {
173 	size_t size;
174 	zend_stream_type old_type;
175 
176 	if (file_handle->type == ZEND_HANDLE_FILENAME) {
177 		if (zend_stream_open(file_handle->filename, file_handle) == FAILURE) {
178 			return FAILURE;
179 		}
180 	}
181 
182 	switch (file_handle->type) {
183 		case ZEND_HANDLE_FD:
184 			file_handle->type = ZEND_HANDLE_FP;
185 			file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb");
186 			/* no break; */
187 		case ZEND_HANDLE_FP:
188 			if (!file_handle->handle.fp) {
189 				return FAILURE;
190 			}
191 			memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap));
192 			file_handle->handle.stream.isatty     = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0;
193 			file_handle->handle.stream.reader     = (zend_stream_reader_t)zend_stream_stdio_reader;
194 			file_handle->handle.stream.closer     = (zend_stream_closer_t)zend_stream_stdio_closer;
195 			file_handle->handle.stream.fsizer     = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
196 			memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
197 			/* no break; */
198 		case ZEND_HANDLE_STREAM:
199 			/* nothing to do */
200 			break;
201 
202 		case ZEND_HANDLE_MAPPED:
203 			file_handle->handle.stream.mmap.pos = 0;
204 			*buf = file_handle->handle.stream.mmap.buf;
205 			*len = file_handle->handle.stream.mmap.len;
206 			return SUCCESS;
207 
208 		default:
209 			return FAILURE;
210 	}
211 
212 	size = zend_stream_fsize(file_handle);
213 	if (size == (size_t)-1) {
214 		return FAILURE;
215 	}
216 
217 	old_type = file_handle->type;
218 	file_handle->type = ZEND_HANDLE_STREAM;  /* we might still be _FP but we need fsize() work */
219 
220 	if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) {
221 #if HAVE_MMAP
222 		size_t page_size = REAL_PAGE_SIZE;
223 
224 		if (file_handle->handle.fp &&
225 		    size != 0 &&
226 		    ((size - 1) % page_size) <= page_size - ZEND_MMAP_AHEAD) {
227 			/*  *buf[size] is zeroed automatically by the kernel */
228 			*buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0);
229 			if (*buf != MAP_FAILED) {
230 				zend_long offset = ftell(file_handle->handle.fp);
231 				file_handle->handle.stream.mmap.map = *buf;
232 
233 				if (offset != -1) {
234 					*buf += offset;
235 					size -= offset;
236 				}
237 				file_handle->handle.stream.mmap.buf = *buf;
238 				file_handle->handle.stream.mmap.len = size;
239 
240 				goto return_mapped;
241 			}
242 		}
243 #endif
244 		file_handle->handle.stream.mmap.map = 0;
245 		file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD);
246 		file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size);
247 	} else {
248 		size_t read, remain = 4*1024;
249 		*buf = emalloc(remain);
250 		size = 0;
251 
252 		while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
253 			size   += read;
254 			remain -= read;
255 
256 			if (remain == 0) {
257 				*buf   = safe_erealloc(*buf, size, 2, 0);
258 				remain = size;
259 			}
260 		}
261 		file_handle->handle.stream.mmap.map = 0;
262 		file_handle->handle.stream.mmap.len = size;
263 		if (size && remain < ZEND_MMAP_AHEAD) {
264 			*buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
265 		}
266 		file_handle->handle.stream.mmap.buf = *buf;
267 	}
268 
269 	if (file_handle->handle.stream.mmap.len == 0) {
270 		*buf = erealloc(*buf, ZEND_MMAP_AHEAD);
271 		file_handle->handle.stream.mmap.buf = *buf;
272 	}
273 
274 	if (ZEND_MMAP_AHEAD) {
275 		memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD);
276 	}
277 #if HAVE_MMAP
278 return_mapped:
279 #endif
280 	file_handle->type = ZEND_HANDLE_MAPPED;
281 	file_handle->handle.stream.mmap.pos        = 0;
282 	file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle;
283 	file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer;
284 	file_handle->handle.stream.handle          = &file_handle->handle.stream;
285 	file_handle->handle.stream.closer          = (zend_stream_closer_t)zend_stream_mmap_closer;
286 
287 	*buf = file_handle->handle.stream.mmap.buf;
288 	*len = file_handle->handle.stream.mmap.len;
289 
290 	return SUCCESS;
291 } /* }}} */
292 
zend_file_handle_dtor(zend_file_handle * fh)293 ZEND_API void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
294 {
295 	switch (fh->type) {
296 		case ZEND_HANDLE_FD:
297 			/* nothing to do */
298 			break;
299 		case ZEND_HANDLE_FP:
300 			fclose(fh->handle.fp);
301 			break;
302 		case ZEND_HANDLE_STREAM:
303 		case ZEND_HANDLE_MAPPED:
304 			if (fh->handle.stream.closer && fh->handle.stream.handle) {
305 				fh->handle.stream.closer(fh->handle.stream.handle);
306 			}
307 			fh->handle.stream.handle = NULL;
308 			break;
309 		case ZEND_HANDLE_FILENAME:
310 			/* We're only supposed to get here when destructing the used_files hash,
311 			 * which doesn't really contain open files, but references to their names/paths
312 			 */
313 			break;
314 	}
315 	if (fh->opened_path) {
316 		zend_string_release(fh->opened_path);
317 		fh->opened_path = NULL;
318 	}
319 	if (fh->free_filename && fh->filename) {
320 		efree((char*)fh->filename);
321 		fh->filename = NULL;
322 	}
323 }
324 /* }}} */
325 
zend_compare_file_handles(zend_file_handle * fh1,zend_file_handle * fh2)326 ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
327 {
328 	if (fh1->type != fh2->type) {
329 		return 0;
330 	}
331 	switch (fh1->type) {
332 		case ZEND_HANDLE_FD:
333 			return fh1->handle.fd == fh2->handle.fd;
334 		case ZEND_HANDLE_FP:
335 			return fh1->handle.fp == fh2->handle.fp;
336 		case ZEND_HANDLE_STREAM:
337 			return fh1->handle.stream.handle == fh2->handle.stream.handle;
338 		case ZEND_HANDLE_MAPPED:
339 			return (fh1->handle.stream.handle == &fh1->handle.stream &&
340 			        fh2->handle.stream.handle == &fh2->handle.stream &&
341 			        fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle)
342 				|| fh1->handle.stream.handle == fh2->handle.stream.handle;
343 		default:
344 			return 0;
345 	}
346 	return 0;
347 } /* }}} */
348 
349 /*
350  * Local variables:
351  * tab-width: 4
352  * c-basic-offset: 4
353  * indent-tabs-mode: t
354  * End:
355  * vim600: sw=4 ts=4 fdm=marker
356  * vim<600: sw=4 ts=4
357  */
358