xref: /php-src/ext/phar/func_interceptors.c (revision aae20cd1)
1 /*
2   +----------------------------------------------------------------------+
3   | phar php single-file executable PHP extension                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) The PHP Group                                          |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP 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   | https://www.php.net/license/3_01.txt                                 |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Gregory Beaver <cellog@php.net>                             |
16   +----------------------------------------------------------------------+
17 */
18 
19 #include "phar_internal.h"
20 
21 #define PHAR_FUNC(name) \
22 	static PHP_NAMED_FUNCTION(name)
23 
PHAR_FUNC(phar_opendir)24 PHAR_FUNC(phar_opendir) /* {{{ */
25 {
26 	char *filename;
27 	size_t filename_len;
28 	zval *zcontext = NULL;
29 
30 	if (!PHAR_G(intercepted)) {
31 		goto skip_phar;
32 	}
33 
34 	if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map))))
35 		&& !HT_IS_INITIALIZED(&cached_phars)) {
36 		goto skip_phar;
37 	}
38 
39 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|r!", &filename, &filename_len, &zcontext) == FAILURE) {
40 		RETURN_THROWS();
41 	}
42 
43 	if (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://")) {
44 		char *arch, *entry;
45 		size_t arch_len, entry_len;
46 		zend_string *fname = zend_get_executed_filename_ex();
47 
48 		/* we are checking for existence of a file within the relative path.  Chances are good that this is
49 		   retrieving something from within the phar archive */
50 		if (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) {
51 			goto skip_phar;
52 		}
53 
54 		if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) {
55 			php_stream_context *context = NULL;
56 			php_stream *stream;
57 			char *name;
58 
59 			efree(entry);
60 			entry = estrndup(filename, filename_len);
61 			/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
62 			entry_len = filename_len;
63 			/* retrieving a file within the current directory, so use this if possible */
64 			entry = phar_fix_filepath(entry, &entry_len, 1);
65 
66 			if (entry[0] == '/') {
67 				spprintf(&name, 4096, "phar://%s%s", arch, entry);
68 			} else {
69 				spprintf(&name, 4096, "phar://%s/%s", arch, entry);
70 			}
71 			efree(entry);
72 			efree(arch);
73 			if (zcontext) {
74 				context = php_stream_context_from_zval(zcontext, 0);
75 			}
76 			stream = php_stream_opendir(name, REPORT_ERRORS, context);
77 			efree(name);
78 			if (!stream) {
79 				RETURN_FALSE;
80 			}
81 			php_stream_to_zval(stream, return_value);
82 			return;
83 		}
84 	}
85 skip_phar:
86 	PHAR_G(orig_opendir)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
87 	return;
88 }
89 /* }}} */
90 
phar_get_name_for_relative_paths(zend_string * filename,bool using_include_path)91 static zend_string* phar_get_name_for_relative_paths(zend_string *filename, bool using_include_path)
92 {
93 	char *arch, *entry;
94 	size_t arch_len, entry_len;
95 	zend_string *fname = zend_get_executed_filename_ex();
96 
97 	/* we are checking for existence of a file within the relative path.  Chances are good that this is
98 	   retrieving something from within the phar archive */
99 	if (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) {
100 		return NULL;
101 	}
102 
103 	if (FAILURE == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) {
104 		return NULL;
105 	}
106 
107 	efree(entry);
108 	entry = NULL;
109 	entry_len = 0;
110 	/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
111 	/* retrieving a file defaults to within the current directory, so use this if possible */
112 	phar_archive_data *phar;
113 	if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
114 		efree(arch);
115 		return NULL;
116 	}
117 
118 	zend_string *name = NULL;
119 	if (using_include_path) {
120 		if (!(name = phar_find_in_include_path(filename, NULL))) {
121 			/* this file is not in the phar, use the original path */
122 			efree(arch);
123 			return NULL;
124 		}
125 	} else {
126 		entry_len = ZSTR_LEN(filename);
127 		entry = phar_fix_filepath(estrndup(ZSTR_VAL(filename), ZSTR_LEN(filename)), &entry_len, 1);
128 		if (entry[0] == '/') {
129 			if (!zend_hash_str_exists(&(phar->manifest), entry + 1, entry_len - 1)) {
130 				/* this file is not in the phar, use the original path */
131 notfound:
132 				efree(entry);
133 				efree(arch);
134 				return NULL;
135 			}
136 		} else {
137 			if (!zend_hash_str_exists(&(phar->manifest), entry, entry_len)) {
138 				goto notfound;
139 			}
140 		}
141 		/* auto-convert to phar:// */
142 		if (entry[0] == '/') {
143 			ZEND_ASSERT(strlen("phar://") + arch_len + entry_len < 4096);
144 			name = zend_string_concat3(
145 				"phar://", strlen("phar://"),
146 				arch, arch_len,
147 				entry, entry_len
148 			);
149 		} else {
150 			name = strpprintf(4096, "phar://%s/%s", arch, entry);
151 		}
152 		efree(entry);
153 	}
154 
155 	efree(arch);
156 	return name;
157 }
158 
PHAR_FUNC(phar_file_get_contents)159 PHAR_FUNC(phar_file_get_contents) /* {{{ */
160 {
161 	zend_string *filename;
162 	zend_string *contents;
163 	bool use_include_path = 0;
164 	zend_long offset = -1;
165 	zend_long maxlen;
166 	bool maxlen_is_null = 1;
167 	zval *zcontext = NULL;
168 
169 	if (!PHAR_G(intercepted)) {
170 		goto skip_phar;
171 	}
172 
173 	if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map))))
174 		&& !HT_IS_INITIALIZED(&cached_phars)) {
175 		goto skip_phar;
176 	}
177 
178 	/* Parse arguments */
179 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "P|br!ll!", &filename, &use_include_path, &zcontext, &offset, &maxlen, &maxlen_is_null) == FAILURE) {
180 		goto skip_phar;
181 	}
182 
183 	if (maxlen_is_null) {
184 		maxlen = (ssize_t) PHP_STREAM_COPY_ALL;
185 	} else if (maxlen < 0) {
186 		zend_argument_value_error(5, "must be greater than or equal to 0");
187 		RETURN_THROWS();
188 	}
189 
190 	if (use_include_path || (!IS_ABSOLUTE_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename)) && !strstr(ZSTR_VAL(filename), "://"))) {
191 		zend_string *name = phar_get_name_for_relative_paths(filename, use_include_path);
192 		if (!name) {
193 			goto skip_phar;
194 		}
195 
196 		php_stream_context *context = NULL;
197 		php_stream *stream;
198 
199 		if (zcontext) {
200 			context = php_stream_context_from_zval(zcontext, 0);
201 		}
202 		stream = php_stream_open_wrapper_ex(ZSTR_VAL(name), "rb", 0 | REPORT_ERRORS, NULL, context);
203 
204 		zend_string_release_ex(name, false);
205 
206 		if (!stream) {
207 			RETURN_FALSE;
208 		}
209 
210 		if (offset > 0 && php_stream_seek(stream, offset, SEEK_SET) < 0) {
211 			php_error_docref(NULL, E_WARNING, "Failed to seek to position " ZEND_LONG_FMT " in the stream", offset);
212 			php_stream_close(stream);
213 			RETURN_FALSE;
214 		}
215 
216 		/* uses mmap if possible */
217 		contents = php_stream_copy_to_mem(stream, maxlen, 0);
218 		if (contents && ZSTR_LEN(contents) > 0) {
219 			RETVAL_STR(contents);
220 		} else if (contents) {
221 			zend_string_release_ex(contents, 0);
222 			RETVAL_EMPTY_STRING();
223 		} else {
224 			RETVAL_FALSE;
225 		}
226 
227 		php_stream_close(stream);
228 		return;
229 	}
230 skip_phar:
231 	PHAR_G(orig_file_get_contents)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
232 	return;
233 }
234 /* }}} */
235 
PHAR_FUNC(phar_readfile)236 PHAR_FUNC(phar_readfile) /* {{{ */
237 {
238 	zend_string *filename;
239 	bool use_include_path = 0;
240 	zval *zcontext = NULL;
241 
242 	if (!PHAR_G(intercepted)) {
243 		goto skip_phar;
244 	}
245 
246 	if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map))))
247 		&& !HT_IS_INITIALIZED(&cached_phars)) {
248 		goto skip_phar;
249 	}
250 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "P|br!", &filename, &use_include_path, &zcontext) == FAILURE) {
251 		goto skip_phar;
252 	}
253 	if (use_include_path || (!IS_ABSOLUTE_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename)) && !strstr(ZSTR_VAL(filename), "://"))) {
254 		zend_string *name = phar_get_name_for_relative_paths(filename, use_include_path);
255 		if (!name) {
256 			goto skip_phar;
257 		}
258 
259 		php_stream *stream;
260 		php_stream_context *context = php_stream_context_from_zval(zcontext, 0);
261 
262 		stream = php_stream_open_wrapper_ex(ZSTR_VAL(name), "rb", 0 | REPORT_ERRORS, NULL, context);
263 
264 		zend_string_release_ex(name, false);
265 		if (stream == NULL) {
266 			RETURN_FALSE;
267 		}
268 		ssize_t size = php_stream_passthru(stream);
269 		php_stream_close(stream);
270 		RETURN_LONG(size);
271 	}
272 
273 skip_phar:
274 	PHAR_G(orig_readfile)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
275 	return;
276 
277 }
278 /* }}} */
279 
PHAR_FUNC(phar_fopen)280 PHAR_FUNC(phar_fopen) /* {{{ */
281 {
282 	zend_string *filename;
283 	char *mode;
284 	size_t mode_len;
285 	bool use_include_path = 0;
286 	zval *zcontext = NULL;
287 
288 	if (!PHAR_G(intercepted)) {
289 		goto skip_phar;
290 	}
291 
292 	if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map))))
293 		&& !HT_IS_INITIALIZED(&cached_phars)) {
294 		/* no need to check, include_path not even specified in fopen/ no active phars */
295 		goto skip_phar;
296 	}
297 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "Ps|br!", &filename, &mode, &mode_len, &use_include_path, &zcontext) == FAILURE) {
298 		goto skip_phar;
299 	}
300 	if (use_include_path || (!IS_ABSOLUTE_PATH(ZSTR_VAL(filename), ZSTR_LEN(filename)) && !strstr(ZSTR_VAL(filename), "://"))) {
301 		zend_string *name = phar_get_name_for_relative_paths(filename, use_include_path);
302 		if (!name) {
303 			goto skip_phar;
304 		}
305 
306 		php_stream *stream;
307 		php_stream_context *context = php_stream_context_from_zval(zcontext, 0);
308 
309 		stream = php_stream_open_wrapper_ex(ZSTR_VAL(name), mode, 0 | REPORT_ERRORS, NULL, context);
310 
311 		zend_string_release_ex(name, false);
312 		if (stream == NULL) {
313 			RETURN_FALSE;
314 		}
315 		php_stream_to_zval(stream, return_value);
316 		if (zcontext) {
317 			Z_ADDREF_P(zcontext);
318 		}
319 		return;
320 	}
321 skip_phar:
322 	PHAR_G(orig_fopen)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
323 	return;
324 }
325 /* }}} */
326 
327 #define IS_LINK_OPERATION(__t) ((__t) == FS_TYPE || (__t) == FS_IS_LINK || (__t) == FS_LSTAT)
328 #define IS_EXISTS_CHECK(__t) ((__t) == FS_EXISTS  || (__t) == FS_IS_W || (__t) == FS_IS_R || (__t) == FS_IS_X || (__t) == FS_IS_FILE || (__t) == FS_IS_DIR || (__t) == FS_IS_LINK)
329 #define IS_ABLE_CHECK(__t) ((__t) == FS_IS_R || (__t) == FS_IS_W || (__t) == FS_IS_X)
330 #define IS_ACCESS_CHECK(__t) (IS_ABLE_CHECK(type) || (__t) == FS_EXISTS)
331 
332 /* {{{ php_stat */
phar_fancy_stat(zend_stat_t * stat_sb,int type,zval * return_value)333 static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value)
334 {
335 	zval stat_dev, stat_ino, stat_mode, stat_nlink, stat_uid, stat_gid, stat_rdev,
336 		 stat_size, stat_atime, stat_mtime, stat_ctime, stat_blksize, stat_blocks;
337 	int rmask=S_IROTH, wmask=S_IWOTH, xmask=S_IXOTH; /* access rights defaults to other */
338 	char *stat_sb_names[13] = {
339 		"dev", "ino", "mode", "nlink", "uid", "gid", "rdev",
340 		"size", "atime", "mtime", "ctime", "blksize", "blocks"
341 	};
342 
343 	if (type >= FS_IS_W && type <= FS_IS_X) {
344 		if(stat_sb->st_uid==getuid()) {
345 			rmask=S_IRUSR;
346 			wmask=S_IWUSR;
347 			xmask=S_IXUSR;
348 		} else if(stat_sb->st_gid==getgid()) {
349 			rmask=S_IRGRP;
350 			wmask=S_IWGRP;
351 			xmask=S_IXGRP;
352 		} else {
353 			int   groups, n, i;
354 			gid_t *gids;
355 
356 			groups = getgroups(0, NULL);
357 			if(groups > 0) {
358 				gids=(gid_t *)safe_emalloc(groups, sizeof(gid_t), 0);
359 				n=getgroups(groups, gids);
360 				for(i=0;i<n;++i){
361 					if(stat_sb->st_gid==gids[i]) {
362 						rmask=S_IRGRP;
363 						wmask=S_IWGRP;
364 						xmask=S_IXGRP;
365 						break;
366 					}
367 				}
368 				efree(gids);
369 			}
370 		}
371 	}
372 
373 	switch (type) {
374 	case FS_PERMS:
375 		RETURN_LONG((zend_long)stat_sb->st_mode);
376 	case FS_INODE:
377 		RETURN_LONG((zend_long)stat_sb->st_ino);
378 	case FS_SIZE:
379 		RETURN_LONG((zend_long)stat_sb->st_size);
380 	case FS_OWNER:
381 		RETURN_LONG((zend_long)stat_sb->st_uid);
382 	case FS_GROUP:
383 		RETURN_LONG((zend_long)stat_sb->st_gid);
384 	case FS_ATIME:
385 		RETURN_LONG((zend_long)stat_sb->st_atime);
386 	case FS_MTIME:
387 		RETURN_LONG((zend_long)stat_sb->st_mtime);
388 	case FS_CTIME:
389 		RETURN_LONG((zend_long)stat_sb->st_ctime);
390 	case FS_TYPE:
391 		if (S_ISLNK(stat_sb->st_mode)) {
392 			RETURN_STRING("link");
393 		}
394 		switch(stat_sb->st_mode & S_IFMT) {
395 		case S_IFDIR: RETURN_STRING("dir");
396 		case S_IFREG: RETURN_STRING("file");
397 		}
398 		php_error_docref(NULL, E_NOTICE, "Unknown file type (%u)", stat_sb->st_mode & S_IFMT);
399 		RETURN_STRING("unknown");
400 	case FS_IS_W:
401 		RETURN_BOOL((stat_sb->st_mode & wmask) != 0);
402 	case FS_IS_R:
403 		RETURN_BOOL((stat_sb->st_mode&rmask)!=0);
404 	case FS_IS_X:
405 		RETURN_BOOL((stat_sb->st_mode&xmask)!=0 && !S_ISDIR(stat_sb->st_mode));
406 	case FS_IS_FILE:
407 		RETURN_BOOL(S_ISREG(stat_sb->st_mode));
408 	case FS_IS_DIR:
409 		RETURN_BOOL(S_ISDIR(stat_sb->st_mode));
410 	case FS_IS_LINK:
411 		RETURN_BOOL(S_ISLNK(stat_sb->st_mode));
412 	case FS_EXISTS:
413 		RETURN_TRUE; /* the false case was done earlier */
414 	case FS_LSTAT:
415 		/* FALLTHROUGH */
416 	case FS_STAT:
417 		array_init(return_value);
418 
419 		ZVAL_LONG(&stat_dev, stat_sb->st_dev);
420 		ZVAL_LONG(&stat_ino, stat_sb->st_ino);
421 		ZVAL_LONG(&stat_mode, stat_sb->st_mode);
422 		ZVAL_LONG(&stat_nlink, stat_sb->st_nlink);
423 		ZVAL_LONG(&stat_uid, stat_sb->st_uid);
424 		ZVAL_LONG(&stat_gid, stat_sb->st_gid);
425 #ifdef HAVE_STRUCT_STAT_ST_RDEV
426 		ZVAL_LONG(&stat_rdev, stat_sb->st_rdev);
427 #else
428 		ZVAL_LONG(&stat_rdev, -1);
429 #endif
430 		ZVAL_LONG(&stat_size, stat_sb->st_size);
431 		ZVAL_LONG(&stat_atime, stat_sb->st_atime);
432 		ZVAL_LONG(&stat_mtime, stat_sb->st_mtime);
433 		ZVAL_LONG(&stat_ctime, stat_sb->st_ctime);
434 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
435 		ZVAL_LONG(&stat_blksize, stat_sb->st_blksize);
436 #else
437 		ZVAL_LONG(&stat_blksize,-1);
438 #endif
439 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
440 		ZVAL_LONG(&stat_blocks, stat_sb->st_blocks);
441 #else
442 		ZVAL_LONG(&stat_blocks,-1);
443 #endif
444 		/* Store numeric indexes in proper order */
445 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_dev);
446 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ino);
447 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mode);
448 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_nlink);
449 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_uid);
450 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_gid);
451 
452 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_rdev);
453 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_size);
454 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_atime);
455 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_mtime);
456 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_ctime);
457 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blksize);
458 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &stat_blocks);
459 
460 		/* Store string indexes referencing the same zval*/
461 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[0], strlen(stat_sb_names[0]), &stat_dev);
462 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[1], strlen(stat_sb_names[1]), &stat_ino);
463 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[2], strlen(stat_sb_names[2]), &stat_mode);
464 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[3], strlen(stat_sb_names[3]), &stat_nlink);
465 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[4], strlen(stat_sb_names[4]), &stat_uid);
466 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[5], strlen(stat_sb_names[5]), &stat_gid);
467 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[6], strlen(stat_sb_names[6]), &stat_rdev);
468 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[7], strlen(stat_sb_names[7]), &stat_size);
469 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[8], strlen(stat_sb_names[8]), &stat_atime);
470 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[9], strlen(stat_sb_names[9]), &stat_mtime);
471 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[10], strlen(stat_sb_names[10]), &stat_ctime);
472 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[11], strlen(stat_sb_names[11]), &stat_blksize);
473 		zend_hash_str_update(Z_ARRVAL_P(return_value), stat_sb_names[12], strlen(stat_sb_names[12]), &stat_blocks);
474 
475 		return;
476 	}
477 	php_error_docref(NULL, E_WARNING, "Didn't understand stat call");
478 	RETURN_FALSE;
479 }
480 /* }}} */
481 
phar_file_stat(const char * filename,size_t filename_length,int type,zif_handler orig_stat_func,INTERNAL_FUNCTION_PARAMETERS)482 static void phar_file_stat(const char *filename, size_t filename_length, int type, zif_handler orig_stat_func, INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
483 {
484 	if (!filename_length) {
485 		RETURN_FALSE;
486 	}
487 
488 	if (!IS_ABSOLUTE_PATH(filename, filename_length) && !strstr(filename, "://")) {
489 		char *arch, *entry;
490 		size_t arch_len, entry_len;
491 		zend_string *fname;
492 		zend_stat_t sb = {0};
493 		phar_entry_info *data = NULL;
494 		phar_archive_data *phar;
495 
496 		fname = zend_get_executed_filename_ex();
497 
498 		/* we are checking for existence of a file within the relative path.  Chances are good that this is
499 		   retrieving something from within the phar archive */
500 		if (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) {
501 			goto skip_phar;
502 		}
503 
504 		if (PHAR_G(last_phar) && ZSTR_LEN(fname) - 7 >= PHAR_G(last_phar_name_len) && !memcmp(ZSTR_VAL(fname) + 7, PHAR_G(last_phar_name), PHAR_G(last_phar_name_len))) {
505 			arch = estrndup(PHAR_G(last_phar_name), PHAR_G(last_phar_name_len));
506 			arch_len = PHAR_G(last_phar_name_len);
507 			entry = estrndup(filename, filename_length);
508 			/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
509 			entry_len = filename_length;
510 			phar = PHAR_G(last_phar);
511 			goto splitted;
512 		}
513 		if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) {
514 
515 			efree(entry);
516 			entry = estrndup(filename, filename_length);
517 			/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
518 			entry_len = filename_length;
519 			if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
520 				efree(arch);
521 				efree(entry);
522 				goto skip_phar;
523 			}
524 splitted:
525 			entry = phar_fix_filepath(entry, &entry_len, 1);
526 			if (entry[0] == '/') {
527 				if (NULL != (data = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) {
528 					efree(entry);
529 					goto stat_entry;
530 				}
531 				goto notfound;
532 			}
533 			if (NULL != (data = zend_hash_str_find_ptr(&(phar->manifest), entry, entry_len))) {
534 				efree(entry);
535 				goto stat_entry;
536 			}
537 			if (zend_hash_str_exists(&(phar->virtual_dirs), entry, entry_len)) {
538 				efree(entry);
539 				efree(arch);
540 				if (IS_EXISTS_CHECK(type)) {
541 					RETURN_TRUE;
542 				}
543 				sb.st_size = 0;
544 				sb.st_mode = 0777;
545 				sb.st_mode |= S_IFDIR; /* regular directory */
546 				sb.st_mtime = phar->max_timestamp;
547 				sb.st_atime = phar->max_timestamp;
548 				sb.st_ctime = phar->max_timestamp;
549 				goto statme_baby;
550 			} else {
551 				char *save;
552 				size_t save_len;
553 
554 notfound:
555 				efree(entry);
556 				save = PHAR_G(cwd);
557 				save_len = PHAR_G(cwd_len);
558 				/* this file is not in the current directory, use the original path */
559 				entry = estrndup(filename, filename_length);
560 				entry_len = filename_length;
561 				PHAR_G(cwd) = "/";
562 				PHAR_G(cwd_len) = 0;
563 				/* clean path without cwd */
564 				entry = phar_fix_filepath(entry, &entry_len, 1);
565 				if (NULL != (data = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) {
566 					PHAR_G(cwd) = save;
567 					PHAR_G(cwd_len) = save_len;
568 					efree(entry);
569 					if (IS_EXISTS_CHECK(type)) {
570 						efree(arch);
571 						RETURN_TRUE;
572 					}
573 					goto stat_entry;
574 				}
575 				if (zend_hash_str_exists(&(phar->virtual_dirs), entry + 1, entry_len - 1)) {
576 					PHAR_G(cwd) = save;
577 					PHAR_G(cwd_len) = save_len;
578 					efree(entry);
579 					efree(arch);
580 					if (IS_EXISTS_CHECK(type)) {
581 						RETURN_TRUE;
582 					}
583 					sb.st_size = 0;
584 					sb.st_mode = 0777;
585 					sb.st_mode |= S_IFDIR; /* regular directory */
586 					sb.st_mtime = phar->max_timestamp;
587 					sb.st_atime = phar->max_timestamp;
588 					sb.st_ctime = phar->max_timestamp;
589 					goto statme_baby;
590 				}
591 				PHAR_G(cwd) = save;
592 				PHAR_G(cwd_len) = save_len;
593 				efree(entry);
594 				efree(arch);
595 				/* Error Occurred */
596 				if (!IS_EXISTS_CHECK(type)) {
597 					php_error_docref(NULL, E_WARNING, "%sstat failed for %s", IS_LINK_OPERATION(type) ? "L" : "", filename);
598 				}
599 				RETURN_FALSE;
600 			}
601 stat_entry:
602 			efree(arch);
603 			if (!data->is_dir) {
604 				sb.st_size = data->uncompressed_filesize;
605 				sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
606 				if (data->link) {
607 					sb.st_mode |= S_IFREG|S_IFLNK; /* regular file */
608 				} else {
609 					sb.st_mode |= S_IFREG; /* regular file */
610 				}
611 				/* timestamp is just the timestamp when this was added to the phar */
612 				sb.st_mtime = data->timestamp;
613 				sb.st_atime = data->timestamp;
614 				sb.st_ctime = data->timestamp;
615 			} else {
616 				sb.st_size = 0;
617 				sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
618 				sb.st_mode |= S_IFDIR; /* regular directory */
619 				if (data->link) {
620 					sb.st_mode |= S_IFLNK;
621 				}
622 				/* timestamp is just the timestamp when this was added to the phar */
623 				sb.st_mtime = data->timestamp;
624 				sb.st_atime = data->timestamp;
625 				sb.st_ctime = data->timestamp;
626 			}
627 
628 statme_baby:
629 			if (!phar->is_writeable) {
630 				sb.st_mode = (sb.st_mode & 0555) | (sb.st_mode & ~0777);
631 			}
632 
633 			sb.st_nlink = 1;
634 			sb.st_rdev = -1;
635 			/* this is only for APC, so use /dev/null device - no chance of conflict there! */
636 			sb.st_dev = 0xc;
637 			/* generate unique inode number for alias/filename, so no phars will conflict */
638 			if (data) {
639 				sb.st_ino = data->inode;
640 			}
641 #ifndef PHP_WIN32
642 			sb.st_blksize = -1;
643 			sb.st_blocks = -1;
644 #endif
645 			phar_fancy_stat(&sb, type, return_value);
646 			return;
647 		}
648 	}
649 skip_phar:
650 	orig_stat_func(INTERNAL_FUNCTION_PARAM_PASSTHRU);
651 	return;
652 }
653 /* }}} */
654 
655 #define PharFileFunction(fname, funcnum, orig) \
656 ZEND_NAMED_FUNCTION(fname) { \
657 	if (!PHAR_G(intercepted)) { \
658 		PHAR_G(orig)(INTERNAL_FUNCTION_PARAM_PASSTHRU); \
659 	} else { \
660 		char *filename; \
661 		size_t filename_len; \
662 		\
663 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) { \
664 			RETURN_THROWS(); \
665 		} \
666 		\
667 		phar_file_stat(filename, filename_len, funcnum, PHAR_G(orig), INTERNAL_FUNCTION_PARAM_PASSTHRU); \
668 	} \
669 }
670 /* }}} */
671 
672 /* {{{ Get file permissions */
PharFileFunction(phar_fileperms,FS_PERMS,orig_fileperms)673 PharFileFunction(phar_fileperms, FS_PERMS, orig_fileperms)
674 /* }}} */
675 
676 /* {{{ Get file inode */
677 PharFileFunction(phar_fileinode, FS_INODE, orig_fileinode)
678 /* }}} */
679 
680 /* {{{ Get file size */
681 PharFileFunction(phar_filesize, FS_SIZE, orig_filesize)
682 /* }}} */
683 
684 /* {{{ Get file owner */
685 PharFileFunction(phar_fileowner, FS_OWNER, orig_fileowner)
686 /* }}} */
687 
688 /* {{{ Get file group */
689 PharFileFunction(phar_filegroup, FS_GROUP, orig_filegroup)
690 /* }}} */
691 
692 /* {{{ Get last access time of file */
693 PharFileFunction(phar_fileatime, FS_ATIME, orig_fileatime)
694 /* }}} */
695 
696 /* {{{ Get last modification time of file */
697 PharFileFunction(phar_filemtime, FS_MTIME, orig_filemtime)
698 /* }}} */
699 
700 /* {{{ Get inode modification time of file */
701 PharFileFunction(phar_filectime, FS_CTIME, orig_filectime)
702 /* }}} */
703 
704 /* {{{ Get file type */
705 PharFileFunction(phar_filetype, FS_TYPE, orig_filetype)
706 /* }}} */
707 
708 /* {{{ Returns true if file can be written */
709 PharFileFunction(phar_is_writable, FS_IS_W, orig_is_writable)
710 /* }}} */
711 
712 /* {{{ Returns true if file can be read */
713 PharFileFunction(phar_is_readable, FS_IS_R, orig_is_readable)
714 /* }}} */
715 
716 /* {{{ Returns true if file is executable */
717 PharFileFunction(phar_is_executable, FS_IS_X, orig_is_executable)
718 /* }}} */
719 
720 /* {{{ Returns true if filename exists */
721 PharFileFunction(phar_file_exists, FS_EXISTS, orig_file_exists)
722 /* }}} */
723 
724 /* {{{ Returns true if file is directory */
725 PharFileFunction(phar_is_dir, FS_IS_DIR, orig_is_dir)
726 /* }}} */
727 
728 PHAR_FUNC(phar_is_file) /* {{{ */
729 {
730 	char *filename;
731 	size_t filename_len;
732 
733 	if (!PHAR_G(intercepted)) {
734 		goto skip_phar;
735 	}
736 
737 	if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map))))
738 		&& !HT_IS_INITIALIZED(&cached_phars)) {
739 		goto skip_phar;
740 	}
741 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
742 		goto skip_phar;
743 	}
744 	if (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://")) {
745 		char *arch, *entry;
746 		size_t arch_len, entry_len;
747 		zend_string *fname = zend_get_executed_filename_ex();
748 
749 		/* we are checking for existence of a file within the relative path.  Chances are good that this is
750 		   retrieving something from within the phar archive */
751 		if (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) {
752 			goto skip_phar;
753 		}
754 
755 		if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) {
756 			phar_archive_data *phar;
757 
758 			efree(entry);
759 			entry = filename;
760 			/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
761 			entry_len = filename_len;
762 			/* retrieving a file within the current directory, so use this if possible */
763 			if (SUCCESS == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
764 				phar_entry_info *etemp;
765 
766 				entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1);
767 				if (entry[0] == '/') {
768 					if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) {
769 						/* this file is not in the current directory, use the original path */
770 found_it:
771 						efree(entry);
772 						efree(arch);
773 						RETURN_BOOL(!etemp->is_dir);
774 					}
775 				} else {
776 					if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry, entry_len))) {
777 						goto found_it;
778 					}
779 				}
780 			}
781 			if (entry != filename) {
782 				efree(entry);
783 			}
784 			efree(arch);
785 			RETURN_FALSE;
786 		}
787 	}
788 skip_phar:
789 	PHAR_G(orig_is_file)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
790 	return;
791 }
792 /* }}} */
793 
PHAR_FUNC(phar_is_link)794 PHAR_FUNC(phar_is_link) /* {{{ */
795 {
796 	char *filename;
797 	size_t filename_len;
798 
799 	if (!PHAR_G(intercepted)) {
800 		goto skip_phar;
801 	}
802 
803 	if ((HT_IS_INITIALIZED(&PHAR_G(phar_fname_map)) && !zend_hash_num_elements(&(PHAR_G(phar_fname_map))))
804 		&& !HT_IS_INITIALIZED(&cached_phars)) {
805 		goto skip_phar;
806 	}
807 	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "p", &filename, &filename_len) == FAILURE) {
808 		goto skip_phar;
809 	}
810 	if (!IS_ABSOLUTE_PATH(filename, filename_len) && !strstr(filename, "://")) {
811 		char *arch, *entry;
812 		size_t arch_len, entry_len;
813 		zend_string *fname = zend_get_executed_filename_ex();
814 
815 		/* we are checking for existence of a file within the relative path.  Chances are good that this is
816 		   retrieving something from within the phar archive */
817 		if (!fname || !zend_string_starts_with_literal_ci(fname, "phar://")) {
818 			goto skip_phar;
819 		}
820 
821 		if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, &entry, &entry_len, 2, 0)) {
822 			phar_archive_data *phar;
823 
824 			efree(entry);
825 			entry = filename;
826 			/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
827 			entry_len = filename_len;
828 			/* retrieving a file within the current directory, so use this if possible */
829 			if (SUCCESS == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
830 				phar_entry_info *etemp;
831 
832 				entry = phar_fix_filepath(estrndup(entry, entry_len), &entry_len, 1);
833 				if (entry[0] == '/') {
834 					if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry + 1, entry_len - 1))) {
835 						/* this file is not in the current directory, use the original path */
836 found_it:
837 						efree(entry);
838 						efree(arch);
839 						RETURN_BOOL(etemp->link);
840 					}
841 				} else {
842 					if (NULL != (etemp = zend_hash_str_find_ptr(&(phar->manifest), entry, entry_len))) {
843 						goto found_it;
844 					}
845 				}
846 			}
847 			efree(entry);
848 			efree(arch);
849 			RETURN_FALSE;
850 		}
851 	}
852 skip_phar:
853 	PHAR_G(orig_is_link)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
854 	return;
855 }
856 /* }}} */
857 
858 /* {{{ Give information about a file or symbolic link */
PharFileFunction(phar_lstat,FS_LSTAT,orig_lstat)859 PharFileFunction(phar_lstat, FS_LSTAT, orig_lstat)
860 /* }}} */
861 
862 /* {{{ Give information about a file */
863 PharFileFunction(phar_stat, FS_STAT, orig_stat)
864 /* }}} */
865 
866 /* {{{ void phar_intercept_functions(void) */
867 void phar_intercept_functions(void)
868 {
869 	if (!PHAR_G(request_init)) {
870 		PHAR_G(cwd) = NULL;
871 		PHAR_G(cwd_len) = 0;
872 	}
873 	PHAR_G(intercepted) = 1;
874 }
875 /* }}} */
876 
877 /* {{{ void phar_release_functions(void) */
phar_release_functions(void)878 void phar_release_functions(void)
879 {
880 	PHAR_G(intercepted) = 0;
881 }
882 /* }}} */
883 
884 /* {{{ void phar_intercept_functions_init(void) */
885 #define PHAR_INTERCEPT(func) \
886 	PHAR_G(orig_##func) = NULL; \
887 	if (NULL != (orig = zend_hash_str_find_ptr(CG(function_table), #func, sizeof(#func)-1))) { \
888 		PHAR_G(orig_##func) = orig->internal_function.handler; \
889 		orig->internal_function.handler = phar_##func; \
890 	}
891 
phar_intercept_functions_init(void)892 void phar_intercept_functions_init(void)
893 {
894 	zend_function *orig;
895 
896 	PHAR_INTERCEPT(fopen);
897 	PHAR_INTERCEPT(file_get_contents);
898 	PHAR_INTERCEPT(is_file);
899 	PHAR_INTERCEPT(is_link);
900 	PHAR_INTERCEPT(is_dir);
901 	PHAR_INTERCEPT(opendir);
902 	PHAR_INTERCEPT(file_exists);
903 	PHAR_INTERCEPT(fileperms);
904 	PHAR_INTERCEPT(fileinode);
905 	PHAR_INTERCEPT(filesize);
906 	PHAR_INTERCEPT(fileowner);
907 	PHAR_INTERCEPT(filegroup);
908 	PHAR_INTERCEPT(fileatime);
909 	PHAR_INTERCEPT(filemtime);
910 	PHAR_INTERCEPT(filectime);
911 	PHAR_INTERCEPT(filetype);
912 	PHAR_INTERCEPT(is_writable);
913 	PHAR_INTERCEPT(is_readable);
914 	PHAR_INTERCEPT(is_executable);
915 	PHAR_INTERCEPT(lstat);
916 	PHAR_INTERCEPT(stat);
917 	PHAR_INTERCEPT(readfile);
918 	PHAR_G(intercepted) = 0;
919 }
920 /* }}} */
921 
922 /* {{{ void phar_intercept_functions_shutdown(void) */
923 #define PHAR_RELEASE(func) \
924 	if (PHAR_G(orig_##func) && NULL != (orig = zend_hash_str_find_ptr(CG(function_table), #func, sizeof(#func)-1))) { \
925 		orig->internal_function.handler = PHAR_G(orig_##func); \
926 	} \
927 	PHAR_G(orig_##func) = NULL;
928 
phar_intercept_functions_shutdown(void)929 void phar_intercept_functions_shutdown(void)
930 {
931 	zend_function *orig;
932 
933 	PHAR_RELEASE(fopen);
934 	PHAR_RELEASE(file_get_contents);
935 	PHAR_RELEASE(is_file);
936 	PHAR_RELEASE(is_dir);
937 	PHAR_RELEASE(opendir);
938 	PHAR_RELEASE(file_exists);
939 	PHAR_RELEASE(fileperms);
940 	PHAR_RELEASE(fileinode);
941 	PHAR_RELEASE(filesize);
942 	PHAR_RELEASE(fileowner);
943 	PHAR_RELEASE(filegroup);
944 	PHAR_RELEASE(fileatime);
945 	PHAR_RELEASE(filemtime);
946 	PHAR_RELEASE(filectime);
947 	PHAR_RELEASE(filetype);
948 	PHAR_RELEASE(is_writable);
949 	PHAR_RELEASE(is_readable);
950 	PHAR_RELEASE(is_executable);
951 	PHAR_RELEASE(lstat);
952 	PHAR_RELEASE(stat);
953 	PHAR_RELEASE(readfile);
954 	PHAR_G(intercepted) = 0;
955 }
956 /* }}} */
957 
958 static struct _phar_orig_functions {
959 	zif_handler orig_fopen;
960 	zif_handler orig_file_get_contents;
961 	zif_handler orig_is_file;
962 	zif_handler orig_is_link;
963 	zif_handler orig_is_dir;
964 	zif_handler orig_opendir;
965 	zif_handler orig_file_exists;
966 	zif_handler orig_fileperms;
967 	zif_handler orig_fileinode;
968 	zif_handler orig_filesize;
969 	zif_handler orig_fileowner;
970 	zif_handler orig_filegroup;
971 	zif_handler orig_fileatime;
972 	zif_handler orig_filemtime;
973 	zif_handler orig_filectime;
974 	zif_handler orig_filetype;
975 	zif_handler orig_is_writable;
976 	zif_handler orig_is_readable;
977 	zif_handler orig_is_executable;
978 	zif_handler orig_lstat;
979 	zif_handler orig_readfile;
980 	zif_handler orig_stat;
981 } phar_orig_functions = {0};
982 
phar_save_orig_functions(void)983 void phar_save_orig_functions(void) /* {{{ */
984 {
985 	phar_orig_functions.orig_fopen             = PHAR_G(orig_fopen);
986 	phar_orig_functions.orig_file_get_contents = PHAR_G(orig_file_get_contents);
987 	phar_orig_functions.orig_is_file           = PHAR_G(orig_is_file);
988 	phar_orig_functions.orig_is_link           = PHAR_G(orig_is_link);
989 	phar_orig_functions.orig_is_dir            = PHAR_G(orig_is_dir);
990 	phar_orig_functions.orig_opendir           = PHAR_G(orig_opendir);
991 	phar_orig_functions.orig_file_exists       = PHAR_G(orig_file_exists);
992 	phar_orig_functions.orig_fileperms         = PHAR_G(orig_fileperms);
993 	phar_orig_functions.orig_fileinode         = PHAR_G(orig_fileinode);
994 	phar_orig_functions.orig_filesize          = PHAR_G(orig_filesize);
995 	phar_orig_functions.orig_fileowner         = PHAR_G(orig_fileowner);
996 	phar_orig_functions.orig_filegroup         = PHAR_G(orig_filegroup);
997 	phar_orig_functions.orig_fileatime         = PHAR_G(orig_fileatime);
998 	phar_orig_functions.orig_filemtime         = PHAR_G(orig_filemtime);
999 	phar_orig_functions.orig_filectime         = PHAR_G(orig_filectime);
1000 	phar_orig_functions.orig_filetype          = PHAR_G(orig_filetype);
1001 	phar_orig_functions.orig_is_writable       = PHAR_G(orig_is_writable);
1002 	phar_orig_functions.orig_is_readable       = PHAR_G(orig_is_readable);
1003 	phar_orig_functions.orig_is_executable     = PHAR_G(orig_is_executable);
1004 	phar_orig_functions.orig_lstat             = PHAR_G(orig_lstat);
1005 	phar_orig_functions.orig_readfile          = PHAR_G(orig_readfile);
1006 	phar_orig_functions.orig_stat              = PHAR_G(orig_stat);
1007 }
1008 /* }}} */
1009 
phar_restore_orig_functions(void)1010 void phar_restore_orig_functions(void) /* {{{ */
1011 {
1012 	PHAR_G(orig_fopen)             = phar_orig_functions.orig_fopen;
1013 	PHAR_G(orig_file_get_contents) = phar_orig_functions.orig_file_get_contents;
1014 	PHAR_G(orig_is_file)           = phar_orig_functions.orig_is_file;
1015 	PHAR_G(orig_is_link)           = phar_orig_functions.orig_is_link;
1016 	PHAR_G(orig_is_dir)            = phar_orig_functions.orig_is_dir;
1017 	PHAR_G(orig_opendir)           = phar_orig_functions.orig_opendir;
1018 	PHAR_G(orig_file_exists)       = phar_orig_functions.orig_file_exists;
1019 	PHAR_G(orig_fileperms)         = phar_orig_functions.orig_fileperms;
1020 	PHAR_G(orig_fileinode)         = phar_orig_functions.orig_fileinode;
1021 	PHAR_G(orig_filesize)          = phar_orig_functions.orig_filesize;
1022 	PHAR_G(orig_fileowner)         = phar_orig_functions.orig_fileowner;
1023 	PHAR_G(orig_filegroup)         = phar_orig_functions.orig_filegroup;
1024 	PHAR_G(orig_fileatime)         = phar_orig_functions.orig_fileatime;
1025 	PHAR_G(orig_filemtime)         = phar_orig_functions.orig_filemtime;
1026 	PHAR_G(orig_filectime)         = phar_orig_functions.orig_filectime;
1027 	PHAR_G(orig_filetype)          = phar_orig_functions.orig_filetype;
1028 	PHAR_G(orig_is_writable)       = phar_orig_functions.orig_is_writable;
1029 	PHAR_G(orig_is_readable)       = phar_orig_functions.orig_is_readable;
1030 	PHAR_G(orig_is_executable)     = phar_orig_functions.orig_is_executable;
1031 	PHAR_G(orig_lstat)             = phar_orig_functions.orig_lstat;
1032 	PHAR_G(orig_readfile)          = phar_orig_functions.orig_readfile;
1033 	PHAR_G(orig_stat)              = phar_orig_functions.orig_stat;
1034 }
1035 /* }}} */
1036