diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 1bac2d1c9883e..7bc3a80bc3e3f 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -411,7 +411,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in uint host_len; /* pre-readonly check, we need to know if this is a data phar */ - if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len TSRMLS_CC)) { + if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", no phar archive specified", url_from); return FAILURE; } @@ -536,8 +536,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_ uint host_len; /* pre-readonly check, we need to know if this is a data phar */ - if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len TSRMLS_CC)) { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\", no phar archive specified", url); + if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) { + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url); return FAILURE; } if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) { diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c index 7f8cd1a3118fb..b456968dae2e9 100644 --- a/ext/phar/func_interceptors.c +++ b/ext/phar/func_interceptors.c @@ -45,7 +45,7 @@ PHAR_FUNC(phar_opendir) /* {{{ */ goto skip_phar; } fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { php_stream_context *context = NULL; php_stream *stream; char *name; @@ -109,7 +109,7 @@ PHAR_FUNC(phar_file_get_contents) /* {{{ */ goto skip_phar; } fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { char *name, *old; phar_archive_data **pphar; @@ -223,7 +223,7 @@ PHAR_FUNC(phar_readfile) /* {{{ */ goto skip_phar; } fname_len = strlen(fname); - if (FAILURE == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (FAILURE == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { goto skip_phar; } @@ -303,7 +303,7 @@ PHAR_FUNC(phar_fopen) /* {{{ */ goto skip_phar; } fname_len = strlen(fname); - if (FAILURE == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (FAILURE == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { goto skip_phar; } @@ -573,7 +573,7 @@ void phar_file_stat(const char *filename, php_stat_len filename_length, int type goto skip_phar; } fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { phar_archive_data **pphar; efree(entry); @@ -827,7 +827,7 @@ PHAR_FUNC(phar_is_file) /* {{{ */ goto skip_phar; } fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { phar_archive_data **pphar; efree(entry); @@ -875,7 +875,7 @@ PHAR_FUNC(phar_is_link) /* {{{ */ goto skip_phar; } fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { phar_archive_data **pphar; efree(entry); diff --git a/ext/phar/phar.c b/ext/phar/phar.c index 8cbb1f8ff4744..2d630568b6049 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -1014,60 +1014,53 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int /** * Create or open a phar for writing */ -int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, char *objname, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ +int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ { - const char *ext_str; - int ext_len, is_data = 0, zip = 0, tar = 0; + const char *ext_str, *z; + int ext_len; if (error) { *error = NULL; } - if (phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len) == SUCCESS) { - - if (ext_len >= sizeof(".zip")-1 && !memcmp((void *) ext_str, (void *) ".zip", sizeof(".zip")-1)) { - zip = 1; - } - - if ((ext_len >= sizeof(".tar")-1 && !memcmp((void *) ext_str, (void *) ".tar", sizeof(".tar")-1)) || (ext_len >= sizeof(".tgz")-1 && !memcmp((void *) ext_str, (void *) ".tgz", sizeof(".tgz")-1))) { - tar = 1; + /* first try to open an existing file */ + if (phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len, !is_data, 0, 1 TSRMLS_CC) == SUCCESS) { + goto check_file; + } + /* next try to create a new file */ + if (FAILURE == phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len, !is_data, 1, 1 TSRMLS_CC)) { + if (error) { + spprintf(error, 0, "Cannot create phar '%s', file extension (or combination) not recognised", fname); } + return FAILURE; + } - if (tar || zip) { - if (objname && strncmp(objname, "PharData", 8) != 0) { - /* Nested exception causes memleak here */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot open '%s' as a Phar object. Use PharData::__construct() for a standard %s archive", fname, tar ? "tar" : "zip"); - return FAILURE; +check_file: + if (phar_open_loaded(fname, fname_len, alias, alias_len, is_data, options, pphar, 0 TSRMLS_CC) == SUCCESS) { + if (is_data && !((*pphar)->is_tar || (*pphar)->is_zip)) { + if (error) { + spprintf(error, 0, "Cannot open '%s' as a PharData object. Use Phar::__construct() for executable archives", fname); } - is_data = 1; - } else { - if (objname && strncmp(objname, "PharData", 8) == 0) { - /* Nested exception causes memleak here */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot open '%s' as a PharData object. Use Phar::__construct() for archives named other than .zip or .tar", fname); + } + if (PHAR_G(readonly) && !is_data && ((*pphar)->is_tar || (*pphar)->is_zip)) { + phar_entry_info *stub; + if (FAILURE == zend_hash_find(&((*pphar)->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&stub)) { + spprintf(error, 0, "'%s' is not a phar archive. Use PharData::__construct() for a standard zip or tar archive", fname); return FAILURE; } } - - } else { - - /* Nested exception causes memleak here */ - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot open '%s' as a %s object, file extension (or combination) not recognised", fname, objname); - return FAILURE; - } - - if (phar_open_loaded(fname, fname_len, alias, alias_len, is_data, options, pphar, 0 TSRMLS_CC) == SUCCESS) { if (pphar && (!PHAR_G(readonly) || is_data)) { (*pphar)->is_writeable = 1; } return SUCCESS; } - if ((ext_len >= sizeof(".phar.zip")-1 && !memcmp((void *) ext_str, (void *) ".phar.zip", sizeof(".phar.zip")-1)) || zip) { + if (ext_len > 3 && (z = memchr(ext_str, 'z', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ip", 2)) { // assume zip-based phar return phar_open_or_create_zip(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC); } - if ((ext_len >= sizeof(".phar.tar")-1 && !memcmp((void *) ext_str, (void *) ".phar.tar", sizeof(".phar.tar")-1)) || tar) { + if (ext_len > 3 && (z = memchr(ext_str, 't', ext_len)) && ((ext_str + ext_len) - z >= 2) && !memcmp(z + 1, "ar", 2)) { // assume tar-based phar return phar_open_or_create_tar(fname, fname_len, alias, alias_len, is_data, options, pphar, error TSRMLS_CC); } @@ -1399,67 +1392,234 @@ static int phar_open_fp(php_stream* fp, char *fname, int fname_len, char *alias, } /* }}} */ -int phar_detect_phar_fname_ext(const char *filename, int check_length, const char **ext_str, int *ext_len) /* {{{ */ +/* + * given the location of the file extension and the start of the file path, + * determine the end of the portion of the path (i.e. /path/to/file.ext/blah + * grabs "/path/to/file.ext" as does the straight /path/to/file.ext), + * stat it to determine if it exists. + * if so, check to see if it is a directory and fail if so + * if not, check to see if its dirname() exists (i.e. "/path/to") and is a directory + * succeed if we are creating the file, otherwise fail. + */ +static int phar_analyze_path(const char *fname, const char *ext, int ext_len, int for_create TSRMLS_DC) +{ + php_stream_statbuf ssb; + char *realpath, old, *a = (char *)(ext + ext_len); + + old = *a; + *a = '\0'; + if ((realpath = expand_filepath(fname, NULL TSRMLS_CC))) { + if (zend_hash_exists(&(PHAR_GLOBALS->phar_fname_map), realpath, strlen(realpath))) { + *a = old; + efree(realpath); + return SUCCESS; + } + efree(realpath); + } + if (SUCCESS == php_stream_stat_path((char *) fname, &ssb)) { + *a = old; + if (ssb.sb.st_mode & S_IFDIR) { + return FAILURE; + } + if (for_create == 1) { + return FAILURE; + } + return SUCCESS; + } else { + char *slash; + + if (!for_create) { + *a = old; + return FAILURE; + } + slash = (char *) strrchr(fname, '/'); + *a = old; + if (slash) { + old = *slash; + *slash = '\0'; + } + if (SUCCESS != php_stream_stat_path((char *) fname, &ssb)) { + if (slash) { + *slash = old; + } else { + if (!(realpath = expand_filepath(fname, NULL TSRMLS_CC))) { + return FAILURE; + } + a = strstr(realpath, fname) + ((ext - fname) + ext_len); + *a = '\0'; + slash = strrchr(realpath, '/'); + if (slash) { + *slash = '\0'; + } else { + efree(realpath); + return FAILURE; + } + if (SUCCESS != php_stream_stat_path(realpath, &ssb)) { + efree(realpath); + return FAILURE; + } + efree(realpath); + if (ssb.sb.st_mode & S_IFDIR) { + return SUCCESS; + } + } + return FAILURE; + } + if (slash) { + *slash = old; + } + if (ssb.sb.st_mode & S_IFDIR) { + return SUCCESS; + } + return FAILURE; + } +} + +/* check for ".phar" in extension */ +static int phar_check_str(const char *fname, const char *ext_str, int ext_len, int executable, int for_create TSRMLS_DC) { - int i; - char end; + char test[51]; const char *pos; -#define EXTINF(check, ext) { check, ext, sizeof(ext)-1 } - const struct { - int check; - const char * const ext; - int len; - } ext_info[] = { - /* longer exts must be specified later */ - EXTINF(1, ".php"), - EXTINF(0, ".phar"), - EXTINF(0, ".tgz"), - EXTINF(0, ".zip"), - EXTINF(0, ".tar"), - EXTINF(0, ".tar.gz"), - EXTINF(0, ".tar.bz2"), - EXTINF(0, ".phar.gz"), - EXTINF(0, ".phar.bz2"), - EXTINF(0, ".phar.php"), - EXTINF(0, ".phar.tar"), - EXTINF(0, ".phar.zip"), - EXTINF(0, ".phar.tar.gz"), - EXTINF(0, ".phar.tar.bz2"), - EXTINF(0, ".phar.tar.php"), - EXTINF(0, ".phar.zip.php")}; + + if (ext_len >= 50) { + return FAILURE; + } + if (executable == 1) { + /* copy "." as well */ + memcpy(test, ext_str - 1, ext_len + 1); + test[ext_len + 1] = '\0'; + /* executable phars must contain ".phar" as a valid extension (phar://.pharmy/oops is invalid) */ + /* (phar://hi/there/.phar/oops is also invalid) */ + if ((pos = strstr(test, ".phar")) && (*(pos - 1) != '/') + && (pos += 5) && (*pos == '\0' || *pos == '/' || *pos == '.')) { + return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC); + } else { + return FAILURE; + } + } + /* data phars need only contain a single non-"." to be valid */ + if (*(ext_str + 1) != '.' && *(ext_str + 1) != '/' && *(ext_str + 1) != '\0') { + return phar_analyze_path(fname, ext_str, ext_len, for_create TSRMLS_CC); + } + return FAILURE; +} + +/* + * if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions + * if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats + * the first extension as the filename extension + * + * if an extension is found, it sets ext_str to the location of the file extension in filename, + * and ext_len to the length of the extension. + * for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells + * the calling function to use "alias" as the phar alias + * + * the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the + * extension rules, not to iterate. + */ +int phar_detect_phar_fname_ext(const char *filename, int check_length, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC) /* {{{ */ +{ + const char *pos, *slash; + int filename_len = strlen(filename); *ext_str = NULL; - for (i = 0; i < sizeof(ext_info) / sizeof(ext_info[0]); ++i) { - pos = strstr(filename, ext_info[i].ext); - /* If pos is not NULL then we found the extension. But we have to check - * to more things. First some extensions cannot be the end of the string - * (e.g. .php). Second when we already found an extension at an earlier - * position, we ignore the new one. In case we found one at the same - * place, we probably found a longer one. As the list is ordered that - * way we do not need to check for a greater length. - */ - if (pos && (/*1*/ !ext_info[i].check || pos[ext_info[i].len] != '\0') - && (/*2*/ !*ext_str || pos <= *ext_str)) { - *ext_str = pos; - *ext_len = ext_info[i].len; + + if (!filename_len || filename_len == 1) { + return FAILURE; + } + phar_request_initialize(TSRMLS_C); + /* first check for extract_list */ + if (zend_hash_num_elements(&(PHAR_GLOBALS->phar_plain_map))) { + for (zend_hash_internal_pointer_reset(&(PHAR_GLOBALS->phar_plain_map)); + zend_hash_has_more_elements(&(PHAR_GLOBALS->phar_plain_map)) == SUCCESS; + zend_hash_move_forward(&(PHAR_GLOBALS->phar_plain_map))) { + char *key; + uint keylen; + ulong intkey; + + if (HASH_KEY_IS_STRING != zend_hash_get_current_key_ex(&(PHAR_GLOBALS->phar_plain_map), &key, &keylen, &intkey, 0, NULL)) { + continue; + } + + if (keylen <= filename_len && !memcmp(key, filename, keylen - 1)) { + /* found plain map, so we grab the extension, if any */ + if (is_complete && keylen != filename_len + 1) { + continue; + } + if (for_create == 1) { + return FAILURE; + } + if (!executable == 1) { + return FAILURE; + } + pos = strrchr(key, '/'); + if (pos) { + pos = filename + (pos - key); + slash = strchr(pos, '.'); + if (slash) { + *ext_str = slash; + *ext_len = keylen - (slash - filename); + return SUCCESS; + } + *ext_str = pos; + *ext_len = -1; + return FAILURE; + } + *ext_str = filename + keylen - 1; + *ext_len = -1; + return FAILURE; + } } } - if (!*ext_str) { - /* We have an alias with no extension, so locate the first / and fail */ - *ext_str = strstr(filename, "/"); - if (*ext_str) { + /* next check for alias in first segment */ + pos = strchr(filename, '/'); + if (pos) { + if (zend_hash_exists(&(PHAR_GLOBALS->phar_alias_map), filename, pos - filename)) { + *ext_str = pos; *ext_len = -1; + return FAILURE; } - return FAILURE; } - if (check_length && (*ext_str)[*ext_len] != '\0') { + + pos = strchr(filename + 1, '.'); +next_extension: + if (!pos) { return FAILURE; } - end = (*ext_str)[*ext_len]; - if (end != '\0' && end != '/' && end != '\\') { - return FAILURE; + + slash = strchr(pos, '/'); + if (!slash) { + /* this is a url like "phar://blah.phar" with no directory */ + *ext_str = pos; + *ext_len = strlen(pos); + /* file extension must contain "phar" */ + switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) { + case SUCCESS : + return SUCCESS; + case FAILURE : + /* we are at the end of the string, so we fail */ + return FAILURE; + } } - return SUCCESS; + /* we've found an extension that ends at a directory separator */ + *ext_str = pos; + *ext_len = slash - pos; + switch (phar_check_str(filename, *ext_str, *ext_len, executable, for_create TSRMLS_CC)) { + case SUCCESS : + return SUCCESS; + case FAILURE : + /* look for more extensions */ + if (is_complete) { + return FAILURE; + } + pos = strchr(pos + 1, '.'); + if (pos) { + *ext_str = NULL; + *ext_len = 0; + } + goto next_extension; + } + return FAILURE; } /* }}} */ @@ -1612,7 +1772,7 @@ char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC) /* {{{ * * This is used by phar_open_url() */ -int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len TSRMLS_DC) /* {{{ */ +int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC) /* {{{ */ { const char *ext_str; int ext_len; @@ -1623,7 +1783,7 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le } ext_len = 0; - if (phar_detect_phar_fname_ext(filename, 0, &ext_str, &ext_len) == FAILURE) { + if (phar_detect_phar_fname_ext(filename, 0, &ext_str, &ext_len, executable, for_create, 0) == FAILURE) { if (ext_len != -1) { if (!ext_str) { /* no / detected, restore arch for error message */ @@ -2698,7 +2858,7 @@ int phar_zend_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); if (fname_len > 7 && !strncasecmp(fname, "phar://", 7)) { - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 1, 0 TSRMLS_CC)) { zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar); efree(arch); efree(entry); diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 4e75bcdbe8f26..547df83167d9c 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -386,7 +386,7 @@ void phar_object_init(TSRMLS_D); int phar_open_entry_file(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC); int phar_open_filename(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC); -int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, char *objname, int options, phar_archive_data** pphar, char **error TSRMLS_DC); +int phar_open_or_create_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC); int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error TSRMLS_DC); int phar_open_compiled_file(char *alias, int alias_len, char **error TSRMLS_DC); int phar_get_archive(phar_archive_data **archive, char *fname, int fname_len, char *alias, int alias_len, char **error TSRMLS_DC); @@ -436,8 +436,8 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC); int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char *path, int path_len, char *mode, char allow_dir, char **error TSRMLS_DC); int phar_flush(phar_archive_data *archive, char *user_stub, long len, int convert, char **error TSRMLS_DC); -int phar_detect_phar_fname_ext(const char *filename, int check_length, const char **ext_str, int *ext_len); -int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len TSRMLS_DC); +int phar_detect_phar_fname_ext(const char *filename, int check_length, const char **ext_str, int *ext_len, int executable, int for_create, int is_complete TSRMLS_DC); +int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_len, char **entry, int *entry_len, int executable, int for_create TSRMLS_DC); typedef enum { pcr_use_query, diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index f3b3fbca1f3cf..852749bac3e38 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -442,7 +442,7 @@ PHP_METHOD(Phar, running) fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { efree(entry); if (retphar) { RETVAL_STRINGL(fname, arch_len + 7, 1); @@ -475,7 +475,7 @@ PHP_METHOD(Phar, mount) fname = zend_get_executed_filename(TSRMLS_C); fname_len = strlen(fname); - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC)) { efree(entry); if (SUCCESS != zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **)&pphar)) { zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, "%s is not a phar archive, cannot mount", arch); @@ -1076,19 +1076,21 @@ PHP_METHOD(Phar, canWrite) } /* }}} */ -/* {{{ proto bool Phar::isValidPharFilename(string filename) +/* {{{ proto bool Phar::isValidPharFilename(string filename[, bool executable = true]) * Returns whether the given filename is a valid phar filename */ PHP_METHOD(Phar, isValidPharFilename) { char *fname; const char *ext_str; int fname_len, ext_len; + zend_bool executable = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &fname, &fname_len, &executable) == FAILURE) { return; } - RETURN_BOOL(phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len) == SUCCESS); + fname_len = executable; + RETURN_BOOL(phar_detect_phar_fname_ext(fname, 1, &ext_str, &ext_len, fname_len, 1, 1) == SUCCESS); } /* }}} */ @@ -1148,7 +1150,18 @@ PHP_METHOD(Phar, __construct) return; } - if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { +#if PHP_VERSION_ID >= 60000 + objname = phar_obj->std.ce->name.s; +#else + objname = phar_obj->std.ce->name; +#endif + if (!strncmp(objname, "PharData", 8)) { + is_data = 1; + } else { + is_data = 0; + } + + if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, !is_data, 2 TSRMLS_CC)) { /* use arch (the basename for the archive) for fname instead of fname */ /* this allows support for RecursiveDirectoryIterator of subdirectories */ save_fname = fname; @@ -1166,13 +1179,7 @@ PHP_METHOD(Phar, __construct) #endif } -#if PHP_VERSION_ID >= 60000 - objname = phar_obj->std.ce->name.s; -#else - objname = phar_obj->std.ce->name; -#endif - - if (phar_open_or_create_filename(fname, fname_len, alias, alias_len, objname, REPORT_ERRORS, &phar_data, &error TSRMLS_CC) == FAILURE) { + if (phar_open_or_create_filename(fname, fname_len, alias, alias_len, is_data, REPORT_ERRORS, &phar_data, &error TSRMLS_CC) == FAILURE) { if (fname == arch) { efree(arch); @@ -3326,7 +3333,7 @@ PHP_METHOD(PharFileInfo, __construct) return; } - if (phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) { + if (phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0 TSRMLS_CC) == FAILURE) { efree(arch); efree(entry); zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, diff --git a/ext/phar/stream.c b/ext/phar/stream.c index a13159ac77e29..d8341b78307fd 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -71,13 +71,13 @@ php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, } return NULL; } - if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len TSRMLS_CC) == FAILURE) { + if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0) TSRMLS_CC) == FAILURE) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { if (arch && !entry) { php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch); arch = NULL; } else { - php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\" (cannot contain .phar.php and .phar.gz/.phar.bz2)", filename); + php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url or non-existent phar \"%s\"", filename); } } return NULL; @@ -116,7 +116,7 @@ php_url* phar_open_url(php_stream_wrapper *wrapper, char *filename, char *mode, php_url_free(resource); return NULL; } - if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, NULL, options, NULL, &error TSRMLS_CC) == FAILURE) + if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, 0, options, NULL, &error TSRMLS_CC) == FAILURE) { if (error) { if (!(options & PHP_STREAM_URL_STAT_QUIET)) { @@ -741,12 +741,10 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char error = NULL; - if ((resource_from = phar_open_url(wrapper, url_from, "rb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from); + if ((resource_from = phar_open_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from); return 0; } - - if (SUCCESS != phar_get_archive(&pfrom, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) { pfrom = NULL; if (error) { @@ -758,9 +756,21 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: write operations disabled by phar.readonly INI setting"); return 0; } + if ((resource_to = phar_open_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) { php_url_free(resource_from); - php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to); + return 0; + } + if (SUCCESS != phar_get_archive(&pto, resource_to->host, strlen(resource_to->host), NULL, 0, &error TSRMLS_CC)) { + if (error) { + efree(error); + } + pto = NULL; + } + if (PHAR_G(readonly) && (!pto || !pto->is_data)) { + php_url_free(resource_from); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: write operations disabled by phar.readonly INI setting"); return 0; } @@ -771,13 +781,6 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char return 0; } - if (SUCCESS != phar_get_archive(&pto, resource_to->host, strlen(resource_to->host), NULL, 0, &error TSRMLS_CC)) { - if (error) { - efree(error); - } - pto = NULL; - } - /* we must have at the very least phar://alias.phar/internalfile.php */ if (!resource_from->scheme || !resource_from->host || !resource_from->path) { php_url_free(resource_from); diff --git a/ext/phar/tests/027.phpt b/ext/phar/tests/027.phpt index 8aa470e589d71..0138735a9386e 100755 --- a/ext/phar/tests/027.phpt +++ b/ext/phar/tests/027.phpt @@ -108,7 +108,8 @@ Warning: opendir(phar://): failed to open dir: phar error: no directory in "phar phar url "phar://" is unknown in %s027.php on line %d bool(false) -Warning: opendir(phar://foo.phar/hi): failed to open dir: phar url "phar://foo.phar/hi" is unknown in %s027.php on line %d +Warning: opendir(phar://foo.phar/hi): failed to open dir: phar error: invalid url or non-existent phar "phar://foo.phar/hi" +phar url "phar://foo.phar/hi" is unknown in %s027.php on line %d bool(false) extract_list test . diff --git a/ext/phar/tests/dir.phpt b/ext/phar/tests/dir.phpt index f87fb0feb71d9..0fe3cedf2530f 100644 --- a/ext/phar/tests/dir.phpt +++ b/ext/phar/tests/dir.phpt @@ -85,7 +85,7 @@ Warning: mkdir(): internal corruption of phar "%soops.phar" (truncated manifest Warning: mkdir(): phar error: cannot create directory "phar://", no phar archive specified in %sdir.php on line %d -Warning: rmdir(): phar error: cannot remove directory "phar://", no phar archive specified in %sdir.php on line %d +Warning: rmdir(): phar error: cannot remove directory "phar://", no phar archive specified, or phar archive does not exist in %sdir.php on line %d Warning: rmdir(): phar error: cannot remove directory "hi" in phar "%sunknown.phar", directory does not exist in %sdir.php on line %d diff --git a/ext/phar/tests/fopen.phpt b/ext/phar/tests/fopen.phpt index 6a936be023e97..4184d416a227b 100644 --- a/ext/phar/tests/fopen.phpt +++ b/ext/phar/tests/fopen.phpt @@ -38,5 +38,5 @@ include $fname; --EXPECTF-- Warning: fopen() expects at least 2 parameters, 0 given in %sfopen.php on line %d hihi -Warning: fopen(phar://%sfopen.phar.php/notfound.txt): failed to open stream: phar error: "notfound.txt" is not a file in phar "%sfopen.phar.php" in phar://%sfopen.phar.php/index.php on line %d +Warning: fopen(notfound.txt): failed to open stream: No such file or directory in phar://%sfopen.phar.php/index.php on line %d ===DONE=== \ No newline at end of file diff --git a/ext/phar/tests/fopen_edgecases.phpt b/ext/phar/tests/fopen_edgecases.phpt index 12c0e77280ea7..4e60c881610fb 100644 --- a/ext/phar/tests/fopen_edgecases.phpt +++ b/ext/phar/tests/fopen_edgecases.phpt @@ -14,6 +14,9 @@ $pname = 'phar://' . $fname; $pname2 = 'phar://' . $fname2; $pname3 = 'phar://' . $fname3; +// create in cwd +chdir(dirname(__FILE__)); +file_put_contents('phar://fopen_edgetest.phar/hi', 'hi'); // append $a = fopen($pname . '/b/c.php', 'a'); // invalid pharname @@ -71,10 +74,11 @@ rename('phar://test.phar/' . basename(__FILE__), 'phar://test.phar/hi'); --CLEAN-- +phar_fname_map), arch, arch_len, (void **) &pphar); @@ -219,7 +219,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d goto doit; } fname = zend_get_executed_filename(TSRMLS_C); - if (SUCCESS != phar_split_fname(fname, strlen(fname), &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS != phar_split_fname(fname, strlen(fname), &arch, &arch_len, &entry, &entry_len, 0, 0 TSRMLS_CC)) { goto doit; } @@ -333,7 +333,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d ret_len = strlen(trypath); /* found phar:// */ - if (SUCCESS != phar_split_fname(trypath, ret_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) { + if (SUCCESS != phar_split_fname(trypath, ret_len, &arch, &arch_len, &entry, &entry_len, 0, 0 TSRMLS_CC)) { return estrndup(trypath, ret_len); } zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **) &pphar);