1: /*
2: +----------------------------------------------------------------------+
3: | Zend Engine |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1998-2014 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,v 1.1.1.4 2014/06/15 20:04:04 misho Exp $ */
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:
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:
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:
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:
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:
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:
103: static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */
104: return file_handle->type == ZEND_HANDLE_MAPPED;
105: } /* }}} */
106:
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:
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:
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:
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: } /* }}} */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>