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