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