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