]> granicus.if.org Git - php/commitdiff
COMPLETELY rework filename extension detection. Now the only requirements are:
authorGreg Beaver <cellog@php.net>
Fri, 18 Apr 2008 04:13:13 +0000 (04:13 +0000)
committerGreg Beaver <cellog@php.net>
Fri, 18 Apr 2008 04:13:13 +0000 (04:13 +0000)
1 - executable phars must contain '.phar' in the filename
2 - non-executable phars must not contain '.phar' and must have an extension of at least 1 character

In addition, phar filenames must exist if opened for read, and the directory containing the phar must exist if opened for creation
if opened for creation, the file must not already exist
[DOC]

13 files changed:
ext/phar/dirstream.c
ext/phar/func_interceptors.c
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/stream.c
ext/phar/tests/027.phpt
ext/phar/tests/dir.phpt
ext/phar/tests/fopen.phpt
ext/phar/tests/fopen_edgecases.phpt
ext/phar/tests/mkdir.phpt
ext/phar/tests/opendir.phpt
ext/phar/util.c

index 1bac2d1c9883ecd3b07ded0b548eee8d24d7f82e..7bc3a80bc3e3f084ac84b1b79fc3d8aee22864e4 100644 (file)
@@ -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)) {
index 7f8cd1a3118fb3ef77748c8ce9f212541666a95e..b456968dae2e96884a3e7a8f80b47b3645f510e8 100644 (file)
@@ -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);
index 8cbb1f8ff4744efe7cbbdb6919f26b6c70333c57..2d630568b6049997c4a38932f456a7a05904975b 100644 (file)
@@ -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);
index 4e75bcdbe8f26b763677ef7a239b9c765c20661f..547df83167d9c02611086536b08e21456bd9110c 100755 (executable)
@@ -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,
index f3b3fbca1f3cfc226023559e98e71e6384c60a2d..852749bac3e38e79c2c0f638be6e22602432778a 100755 (executable)
@@ -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,
index a13159ac77e2915ea9919eaded280b45c9ed218d..d8341b78307fd5a83fbaed9fc606132b5769632d 100644 (file)
@@ -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);
index 8aa470e589d7116e458780406edc931333ef0b38..0138735a9386ec0b16fbbca0fa98a93ea8570940 100755 (executable)
@@ -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
 .
index f87fb0feb71d94ca5d0f7d78bf052f47ca241b03..0fe3cedf2530f2768007d0a3f74af98747bf7bdf 100644 (file)
@@ -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
 
index 6a936be023e972c088da0696135a2f22b4ed686d..4184d416a227b299c1ebb17a8b2af5eb1e2d8614 100644 (file)
@@ -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
index 12c0e77280ea7425360cbde0b25813afe0e4be9e..4e60c881610fb69ac56123c1c2ea48d1d8ae621a 100644 (file)
@@ -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--
 <?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php'); ?>
 <?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.phar.php'); ?>
+<?php unlink(dirname(__FILE__) . '/fopen_edgetest.phar');
 --EXPECTF--
 Warning: fopen(phar://%sfopen_edgecases.phar.php/b/c.php): failed to open stream: phar error: open mode append not supported in %sfopen_edgecases.php on line %d
 
-Warning: fopen(phar://%sfopen_edgecases.phar.php.phar.gz): failed to open stream: phar error: invalid url "phar://%sfopen_edgecases.phar.php.phar.gz" (cannot contain .phar.php and .phar.gz/.phar.bz2) in %sfopen_edgecases.php on line %d
+Warning: fopen(phar://%sfopen_edgecases.phar.php.phar.gz): failed to open stream: phar error: invalid url or non-existent phar "phar://%sfopen_edgecases.phar.php.phar.gz" in %sfopen_edgecases.php on line %d
 bool(false)
 
 Warning: fopen(phar://%sfopen_edgecases.2.phar.php/hi): failed to open stream: internal corruption of phar "%sfopen_edgecases.2.phar.php" (truncated manifest at stub end) in %sfopen_edgecases.php on line %d
@@ -107,7 +111,7 @@ Warning: unlink(): phar error: no directory in "phar://", must have at least pha
 
 Warning: unlink(): phar error: unlink failed in %sfopen_edgecases.php on line %d
 
-Warning: unlink(): unable to open phar for reading "foo.phar" in %sfopen_edgecases.php on line %d
+Warning: unlink(): phar error: invalid url or non-existent phar "phar://foo.phar" in %sfopen_edgecases.php on line %d
 
 Warning: unlink(): phar error: unlink failed in %sfopen_edgecases.php on line %d
 
@@ -115,15 +119,15 @@ Warning: unlink(): phar error: "phar://test.phar/fopen_edgecases.php" cannot be
 
 Warning: unlink(): unlink of "phar://%sfopen_edgecases.phar.php/oops" failed, file does not exist in %sfopen_edgecases.php on line %d
 
-Warning: rename(): phar error: cannot rename "phar://" to "phar://": invalid url "phar://" in %sfopen_edgecases.php on line %d
+Warning: rename(): phar error: cannot rename "phar://" to "phar://": invalid or non-writable url "phar://" in %sfopen_edgecases.php on line %d
 
-Warning: rename(): phar error: cannot rename "phar://%sfopen_edgecases.phar.php/hi" to "phar://": invalid url "phar://" in %sfopen_edgecases.php on line %d
+Warning: rename(): phar error: cannot rename "phar://%sfopen_edgecases.phar.php/hi" to "phar://": invalid or non-writable url "phar://" in %sfopen_edgecases.php on line %d
 
-Warning: rename(): phar error: cannot rename "phar://foo.phar/hi" to "phar://": invalid url "phar://foo.phar/hi" in %sfopen_edgecases.php on line %d
+Warning: rename(): phar error: cannot rename "phar://foo.phar/hi" to "phar://": invalid or non-writable url "phar://" in %sfopen_edgecases.php on line %d
 
 Warning: rename(): phar error: cannot rename "phar://%sfopen_edgecases.phar.php/hi" to "phar://foo.phar/hi", not within the same phar archive in %sfopen_edgecases.php on line %d
 
-Warning: rename(): phar error: write operations disabled by phar.readonly INI setting in %sfopen_edgecases.php on line %d
+Warning: rename(): phar error: cannot rename "phar://%sfopen_edgecases.phar.php/hi" to "phar://%sfopen_edgecases.phar.php/there": invalid or non-writable url "phar://%sfopen_edgecases.phar.php/hi" in %sfopen_edgecases.php on line %d
 
 Warning: rename(): phar error: cannot rename "phar://test.phar/fopen_edgecases.php" to "phar://test.phar/hi" from extracted phar archive in %sfopen_edgecases.php on line %d
 
index 13b5b44943249136e7a0c480fb5a88b335faa93d..2adf1ccd287121a643e0df5d0e8e6a69db1ab42d 100644 (file)
@@ -31,7 +31,7 @@ Warning: mkdir(): phar error: cannot create directory "" in phar "foo.phar", pha
 
 Warning: mkdir(): phar error: cannot create directory "a" in phar "%smkdir.phar.php", phar error: path "a" exists and is a not a directory in %smkdir.php on line %d
 
-Warning: rmdir(): phar error: cannot remove directory "phar://", no phar archive specified in %smkdir.php on line %d
+Warning: rmdir(): phar error: cannot remove directory "phar://", no phar archive specified, or phar archive does not exist in %smkdir.php on line %d
 
 Warning: rmdir(): phar error: cannot remove directory "" in phar "foo.phar", directory does not exist in %smkdir.php on line %d
 
index 2ec92caf9be79d636de077cfffc52297007e5866..4d2efa3788f3841c5b7dc892d70d9bc6b01fb0d0 100644 (file)
@@ -39,7 +39,8 @@ file1.txtfile2.txtfile3.txt
 Warning: opendir(phar://): failed to open dir: phar error: no directory in "phar://", must have at least phar:/// for root directory (always use full path to a new phar)
 phar url "phar://" is unknown in %sopendir.php on line %d
 
-Warning: opendir(phar://hi.phar): failed to open dir: phar url "phar://hi.phar" is unknown in %sopendir.php on line %d
+Warning: opendir(phar://hi.phar): failed to open dir: phar error: invalid url or non-existent phar "phar://hi.phar"
+phar url "phar://hi.phar" is unknown in %sopendir.php on line %d
 
 Warning: opendir(phar://hi.phar/oopsie/daisy/): failed to open dir: phar error: file "%soopsie/daisy" extracted from "hi.phar" could not be opened in %sopendir.php on line %d
 ===DONE===
\ No newline at end of file
index 641b54f863cefc82dc62f1e1594d8f429b046c6d..1099464fce73ba2845a4521049d9441ae994bcc7 100644 (file)
@@ -161,7 +161,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d
                return phar_save_resolve_path(filename, filename_len TSRMLS_CC);
        }
        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)) {
                return phar_save_resolve_path(filename, filename_len TSRMLS_CC);
        }
        if (*filename == '.') {
@@ -193,7 +193,7 @@ char *phar_find_in_include_path(char *filename, int filename_len, phar_archive_d
                ret_len = strlen(ret);
                /* found phar:// */
 
-               if (SUCCESS != phar_split_fname(ret, ret_len, &arch, &arch_len, &entry, &entry_len TSRMLS_CC)) {
+               if (SUCCESS != phar_split_fname(ret, ret_len, &arch, &arch_len, &entry, &entry_len, 0, 0 TSRMLS_CC)) {
                        return ret;
                }
                zend_hash_find(&(PHAR_GLOBALS->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 @@ not_stream:
                                                        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);