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