xref: /PHP-8.4/Zend/zend_stream.c (revision 2f4973fd)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 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 #include "zend.h"
23 #include "zend_compile.h"
24 #include "zend_stream.h"
25 
26 ZEND_DLIMPORT int isatty(int fd);
27 
zend_stream_stdio_reader(void * handle,char * buf,size_t len)28 static ssize_t zend_stream_stdio_reader(void *handle, char *buf, size_t len) /* {{{ */
29 {
30 	return fread(buf, 1, len, (FILE*)handle);
31 } /* }}} */
32 
zend_stream_stdio_closer(void * handle)33 static void zend_stream_stdio_closer(void *handle) /* {{{ */
34 {
35 	if (handle && (FILE*)handle != stdin) {
36 		fclose((FILE*)handle);
37 	}
38 } /* }}} */
39 
zend_stream_stdio_fsizer(void * handle)40 static size_t zend_stream_stdio_fsizer(void *handle) /* {{{ */
41 {
42 	zend_stat_t buf = {0};
43 	if (handle && zend_fstat(fileno((FILE*)handle), &buf) == 0) {
44 #ifdef S_ISREG
45 		if (!S_ISREG(buf.st_mode)) {
46 			return 0;
47 		}
48 #endif
49 		return buf.st_size;
50 	}
51 	return -1;
52 } /* }}} */
53 
zend_stream_fsize(zend_file_handle * file_handle)54 static size_t zend_stream_fsize(zend_file_handle *file_handle) /* {{{ */
55 {
56 	ZEND_ASSERT(file_handle->type == ZEND_HANDLE_STREAM);
57 	if (file_handle->handle.stream.isatty) {
58 		return 0;
59 	}
60 	return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle);
61 } /* }}} */
62 
zend_stream_init_fp(zend_file_handle * handle,FILE * fp,const char * filename)63 ZEND_API void zend_stream_init_fp(zend_file_handle *handle, FILE *fp, const char *filename) {
64 	memset(handle, 0, sizeof(zend_file_handle));
65 	handle->type = ZEND_HANDLE_FP;
66 	handle->handle.fp = fp;
67 	handle->filename = filename ? zend_string_init(filename, strlen(filename), 0) : NULL;
68 }
69 
zend_stream_init_filename(zend_file_handle * handle,const char * filename)70 ZEND_API void zend_stream_init_filename(zend_file_handle *handle, const char *filename) {
71 	memset(handle, 0, sizeof(zend_file_handle));
72 	handle->type = ZEND_HANDLE_FILENAME;
73 	handle->filename = filename ? zend_string_init(filename, strlen(filename), 0) : NULL;
74 }
75 
zend_stream_init_filename_ex(zend_file_handle * handle,zend_string * filename)76 ZEND_API void zend_stream_init_filename_ex(zend_file_handle *handle, zend_string *filename) {
77 	memset(handle, 0, sizeof(zend_file_handle));
78 	handle->type = ZEND_HANDLE_FILENAME;
79 	handle->filename = zend_string_copy(filename);
80 }
81 
zend_stream_open(zend_file_handle * handle)82 ZEND_API zend_result zend_stream_open(zend_file_handle *handle) /* {{{ */
83 {
84 	zend_string *opened_path;
85 
86 	ZEND_ASSERT(handle->type == ZEND_HANDLE_FILENAME);
87 	if (zend_stream_open_function) {
88 		return zend_stream_open_function(handle);
89 	}
90 
91 	handle->handle.fp = zend_fopen(handle->filename, &opened_path);
92 	if (!handle->handle.fp) {
93 		return FAILURE;
94 	}
95 	handle->type = ZEND_HANDLE_FP;
96 	return SUCCESS;
97 } /* }}} */
98 
zend_stream_getc(zend_file_handle * file_handle)99 static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
100 {
101 	char buf;
102 
103 	if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
104 		return (int)buf;
105 	}
106 	return EOF;
107 } /* }}} */
108 
zend_stream_read(zend_file_handle * file_handle,char * buf,size_t len)109 static ssize_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
110 {
111 	if (file_handle->handle.stream.isatty) {
112 		int c = '*';
113 		size_t n;
114 
115 		for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n)  {
116 			buf[n] = (char)c;
117 		}
118 		if (c == '\n') {
119 			buf[n++] = (char)c;
120 		}
121 
122 		return n;
123 	}
124 	return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
125 } /* }}} */
126 
zend_stream_fixup(zend_file_handle * file_handle,char ** buf,size_t * len)127 ZEND_API zend_result zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
128 {
129 	size_t file_size;
130 
131 	if (file_handle->buf) {
132 		*buf = file_handle->buf;
133 		*len = file_handle->len;
134 		return SUCCESS;
135 	}
136 
137 	if (file_handle->type == ZEND_HANDLE_FILENAME) {
138 		if (zend_stream_open(file_handle) == FAILURE) {
139 			return FAILURE;
140 		}
141 	}
142 
143 	if (file_handle->type == ZEND_HANDLE_FP) {
144 		if (!file_handle->handle.fp) {
145 			return FAILURE;
146 		}
147 
148 		file_handle->type = ZEND_HANDLE_STREAM;
149 		file_handle->handle.stream.handle = file_handle->handle.fp;
150 		file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle));
151 		file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
152 		file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
153 		file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
154 	}
155 
156 	file_size = zend_stream_fsize(file_handle);
157 	if (file_size == (size_t)-1) {
158 		return FAILURE;
159 	}
160 
161 	if (file_size) {
162 		ssize_t read;
163 		size_t size = 0;
164 		*buf = safe_emalloc(1, file_size, ZEND_MMAP_AHEAD);
165 		while ((read = zend_stream_read(file_handle, *buf + size, file_size - size)) > 0) {
166 			size += read;
167 		}
168 		if (read < 0) {
169 			efree(*buf);
170 			return FAILURE;
171 		}
172 		file_handle->buf = *buf;
173 		file_handle->len = size;
174 	} else {
175 		size_t size = 0, remain = 4*1024;
176 		ssize_t read;
177 		*buf = emalloc(remain);
178 
179 		while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
180 			size   += read;
181 			remain -= read;
182 
183 			if (remain == 0) {
184 				*buf   = safe_erealloc(*buf, size, 2, 0);
185 				remain = size;
186 			}
187 		}
188 		if (read < 0) {
189 			efree(*buf);
190 			return FAILURE;
191 		}
192 
193 		file_handle->len = size;
194 		if (size && remain < ZEND_MMAP_AHEAD) {
195 			*buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
196 		}
197 		file_handle->buf = *buf;
198 	}
199 
200 	if (file_handle->len == 0) {
201 		*buf = erealloc(*buf, ZEND_MMAP_AHEAD);
202 		file_handle->buf = *buf;
203 	}
204 
205 	memset(file_handle->buf + file_handle->len, 0, ZEND_MMAP_AHEAD);
206 
207 	*buf = file_handle->buf;
208 	*len = file_handle->len;
209 
210 	return SUCCESS;
211 } /* }}} */
212 
zend_file_handle_dtor(zend_file_handle * fh)213 static void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
214 {
215 	switch (fh->type) {
216 		case ZEND_HANDLE_FP:
217 			if (fh->handle.fp) {
218 				fclose(fh->handle.fp);
219 				fh->handle.fp = NULL;
220 			}
221 			break;
222 		case ZEND_HANDLE_STREAM:
223 			if (fh->handle.stream.closer && fh->handle.stream.handle) {
224 				fh->handle.stream.closer(fh->handle.stream.handle);
225 			}
226 			fh->handle.stream.handle = NULL;
227 			break;
228 		case ZEND_HANDLE_FILENAME:
229 			/* We're only supposed to get here when destructing the used_files hash,
230 			 * which doesn't really contain open files, but references to their names/paths
231 			 */
232 			break;
233 	}
234 	if (fh->opened_path) {
235 		zend_string_release_ex(fh->opened_path, 0);
236 		fh->opened_path = NULL;
237 	}
238 	if (fh->buf) {
239 		efree(fh->buf);
240 		fh->buf = NULL;
241 	}
242 	if (fh->filename) {
243 		zend_string_release(fh->filename);
244 		fh->filename = NULL;
245 	}
246 }
247 /* }}} */
248 
249 /* return int to be compatible with Zend linked list API */
zend_compare_file_handles(zend_file_handle * fh1,zend_file_handle * fh2)250 static int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
251 {
252 	if (fh1->type != fh2->type) {
253 		return 0;
254 	}
255 	switch (fh1->type) {
256 		case ZEND_HANDLE_FILENAME:
257 			return zend_string_equals(fh1->filename, fh2->filename);
258 		case ZEND_HANDLE_FP:
259 			return fh1->handle.fp == fh2->handle.fp;
260 		case ZEND_HANDLE_STREAM:
261 			return fh1->handle.stream.handle == fh2->handle.stream.handle;
262 		default:
263 			return 0;
264 	}
265 	return 0;
266 } /* }}} */
267 
zend_destroy_file_handle(zend_file_handle * file_handle)268 ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) /* {{{ */
269 {
270 	if (file_handle->in_list) {
271 		zend_llist_del_element(&CG(open_files), file_handle, (int (*)(void *, void *)) zend_compare_file_handles);
272 		/* zend_file_handle_dtor() operates on the copy, so we have to NULLify the original here */
273 		file_handle->opened_path = NULL;
274 		file_handle->filename = NULL;
275 	} else {
276 		zend_file_handle_dtor(file_handle);
277 	}
278 } /* }}} */
279 
zend_stream_init(void)280 void zend_stream_init(void) /* {{{ */
281 {
282 	zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) zend_file_handle_dtor, 0);
283 } /* }}} */
284 
zend_stream_shutdown(void)285 void zend_stream_shutdown(void) /* {{{ */
286 {
287 	zend_llist_destroy(&CG(open_files));
288 } /* }}} */
289