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