]> granicus.if.org Git - php/commitdiff
implement symbolic link support within a tar-based phar archive
authorGreg Beaver <cellog@php.net>
Mon, 21 Apr 2008 06:17:51 +0000 (06:17 +0000)
committerGreg Beaver <cellog@php.net>
Mon, 21 Apr 2008 06:17:51 +0000 (06:17 +0000)
this also resulted in a major fix for mounted directories, which were recycling the 'link' field which
could cause stupid conflicts with actual links, so move that to new 'tmp' field.

ext/phar/dirstream.c
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/stream.c
ext/phar/tar.c
ext/phar/tests/tar/files/corrupt_tarmaker.php.inc
ext/phar/tests/tar/links.phpt
ext/phar/util.c
ext/phar/zip.c

index 7bc3a80bc3e3f084ac84b1b79fc3d8aee22864e4..1d372829fdcd3714aa0177a72c41bcf0eaa733d3 100644 (file)
@@ -364,7 +364,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char
                /*if (entry->is_mounted) {
                         external directory, TODO: construct an internal dirstream based on this actual dir's dirstream
                        php_url_free(resource);
-                       return php_stream_opendir(entry->link, options, context);
+                       return php_stream_opendir(entry->tmp, options, context);
                }*/
                internal_file = estrdup(internal_file);
                php_url_free(resource);
index b429b3aab7691c6891c016e0169bcdeceab9e5de..7b5e3e32295b031a9f605957f13238d17873ffbe 100644 (file)
@@ -330,6 +330,10 @@ void destroy_phar_manifest_entry(void *pDest) /* {{{ */
                efree(entry->link);
                entry->link = 0;
        }
+       if (entry->tmp) {
+               efree(entry->tmp);
+               entry->tmp = 0;
+       }
 }
 /* }}} */
 
@@ -1781,7 +1785,9 @@ char *phar_fix_filepath(char *path, int *new_len, int use_cwd 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;
+#ifdef PHP_WIN32
        char *save;
+#endif
        int ext_len, free_filename = 0;
 
        if (!strncasecmp(filename, "phar://", 7)) {
@@ -1800,7 +1806,11 @@ int phar_split_fname(char *filename, int filename_len, char **arch, int *arch_le
                if (ext_len != -1) {
                        if (!ext_str) {
                                /* no / detected, restore arch for error message */
+#ifdef PHP_WIN32
                                *arch = save;
+#else
+                               *arch = filename;
+#endif
                        }
                        if (free_filename) {
                                efree(filename);
@@ -2246,7 +2256,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
                if ((oldfile && !entry->is_modified) || entry->is_dir) {
                        continue;
                }
-               if (!phar_get_efp(entry TSRMLS_CC)) {
+               if (!phar_get_efp(entry, 0 TSRMLS_CC)) {
                        /* re-open internal file pointer just-in-time */
                        newentry = phar_open_jit(phar, entry, oldfile, error, 0 TSRMLS_CC);
                        if (!newentry) {
@@ -2257,8 +2267,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
                        }
                        entry = newentry;
                }
-               file = phar_get_efp(entry TSRMLS_CC);
-               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+               file = phar_get_efp(entry, 0 TSRMLS_CC);
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
                        if (closeoldfile) {
                                php_stream_close(oldfile);
                        }
@@ -2313,7 +2323,7 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
                        return EOF;
                }
                php_stream_flush(file);
-               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
                        if (closeoldfile) {
                                php_stream_close(oldfile);
                        }
@@ -2507,8 +2517,8 @@ int phar_flush(phar_archive_data *phar, char *user_stub, long len, int convert,
                        file = entry->cfp;
                        php_stream_rewind(file);
                } else {
-                       file = phar_get_efp(entry TSRMLS_CC);
-                       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+                       file = phar_get_efp(entry, 0 TSRMLS_CC);
+                       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
                                if (closeoldfile) {
                                        php_stream_close(oldfile);
                                }
index b6f6bf49d6c175476802965c19399bce99c44b1d..aab5b3fd02528b9b22b82dc3baf2acc81abfd435 100755 (executable)
@@ -244,6 +244,7 @@ typedef struct _phar_entry_info {
        /* this flag is used for mounted entries (external files mapped to location
           inside a phar */
        int                      is_mounted:1;
+       char                     *tmp;
        /* used when iterating */
        int                      is_temp_dir:1;
        phar_archive_data        *phar;
@@ -404,10 +405,11 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry,
                                      char **error, int for_write TSRMLS_DC);
 int phar_parse_metadata(char **buffer, zval **metadata, int zip_metadata_len TSRMLS_DC);
 void destroy_phar_manifest_entry(void *pDest);
-int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position TSRMLS_DC);
-php_stream *phar_get_efp(phar_entry_info *entry TSRMLS_DC);
+int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position, int follow_links TSRMLS_DC);
+php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC);
 int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC);
-int phar_open_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
+int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC);
+phar_entry_info *phar_get_link_source(phar_entry_info *entry TSRMLS_DC);
 int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry, char **error TSRMLS_DC);
 int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC);
 int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC);
index a1b01a4a1788c6a058639fc5c78e77d988673a84..d486a2888bc4a90db69bf230e515bde1f536b7b1 100755 (executable)
@@ -237,7 +237,7 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch
                        }
 
                        /* prepare to output  */
-                       if (!phar_get_efp(phar->internal_file TSRMLS_CC)) {
+                       if (!phar_get_efp(phar->internal_file, 1 TSRMLS_CC)) {
                                char *error;
                                if (!phar_open_jit(phar->phar, phar->internal_file, phar->phar->fp, &error, 0 TSRMLS_CC)) {
                                        if (error) {
@@ -246,10 +246,10 @@ static int phar_file_action(phar_entry_data *phar, char *mime_type, int code, ch
                                        }
                                        return -1;
                                }
-                               phar->fp = phar_get_efp(phar->internal_file TSRMLS_CC);
+                               phar->fp = phar_get_efp(phar->internal_file, 1 TSRMLS_CC);
                                phar->zero = phar->internal_file->offset;
                        }
-                       phar_seek_efp(phar->internal_file, 0, SEEK_SET, 0 TSRMLS_CC);
+                       phar_seek_efp(phar->internal_file, 0, SEEK_SET, 0, 1 TSRMLS_CC);
                        do {
                                got = php_stream_read(phar->fp, buf, MIN(8192, phar->internal_file->uncompressed_filesize - phar->position));
                                PHPWRITE(buf, got);
@@ -1580,8 +1580,9 @@ static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS
 {
        char *error;
        off_t offset;
+       phar_entry_info *link;
 
-       if (FAILURE == phar_open_entry_fp(entry, &error TSRMLS_CC)) {
+       if (FAILURE == phar_open_entry_fp(entry, &error, 1 TSRMLS_CC)) {
                if (error) {
                        zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
                                "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
@@ -1593,9 +1594,13 @@ static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS
                return FAILURE;
        }
        /* copy old contents in entirety */
-       phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
+       phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC);
        offset = php_stream_tell(fp);
-       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry TSRMLS_CC), fp, entry->uncompressed_filesize)) {
+       link = phar_get_link_source(entry TSRMLS_CC);
+       if (!link) {
+               link = entry;
+       }
+       if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) {
                zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
                        "Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename);
                return FAILURE;
@@ -3642,6 +3647,7 @@ PHP_METHOD(PharFileInfo, getContent)
 {
        char *error;
        php_stream *fp;
+       phar_entry_info *link;
        PHAR_ENTRY_OBJECT();
 
        if (entry_obj->ent.entry->is_dir) {
@@ -3649,20 +3655,27 @@ PHP_METHOD(PharFileInfo, getContent)
                        "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\" is a directory", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
                return;
        }
-       if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error TSRMLS_CC)) {
+       link = phar_get_link_source(entry_obj->ent.entry TSRMLS_CC);
+       if (!link) {
+               link = entry_obj->ent.entry;
+       }
+       if (SUCCESS != phar_open_entry_fp(link, &error, 0 TSRMLS_CC)) {
                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
                        "Phar error: Cannot retrieve contents, \"%s\" in phar \"%s\": %s", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname, error);
                efree(error);
                return;
        }
-       if (!(fp = phar_get_efp(entry_obj->ent.entry TSRMLS_CC))) {
+       if (!(fp = phar_get_efp(link, 0 TSRMLS_CC))) {
                zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
                        "Phar error: Cannot retrieve contents of \"%s\" in phar \"%s\"", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname);
                return;
        }
-       phar_seek_efp(entry_obj->ent.entry, 0, SEEK_SET, 0 TSRMLS_CC);
+       phar_seek_efp(link, 0, SEEK_SET, 0, 0 TSRMLS_CC);
        Z_TYPE_P(return_value) = IS_STRING;
-       Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), entry_obj->ent.entry->uncompressed_filesize, 0);
+       Z_STRLEN_P(return_value) = php_stream_copy_to_mem(fp, &(Z_STRVAL_P(return_value)), link->uncompressed_filesize, 0);
+       if (!Z_STRVAL_P(return_value)) {
+               Z_STRVAL_P(return_value) = estrndup("", 0);
+       }
 }
 /* }}} */
 
@@ -3713,7 +3726,7 @@ PHP_METHOD(PharFileInfo, compress)
                                        return;
                                }
                                /* decompress this file indirectly */
-                               if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error TSRMLS_CC)) {
+                               if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) {
                                        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
                                                "Phar error: Cannot decompress bzip2-compressed file \"%s\" in phar \"%s\" in order to compress with gzip: %s", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname, error);
                                        efree(error);
@@ -3741,7 +3754,7 @@ PHP_METHOD(PharFileInfo, compress)
                                        return;
                                }
                                /* decompress this file indirectly */
-                               if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error TSRMLS_CC)) {
+                               if (SUCCESS != phar_open_entry_fp(entry_obj->ent.entry, &error, 1 TSRMLS_CC)) {
                                        zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
                                                "Phar error: Cannot decompress gzip-compressed file \"%s\" in phar \"%s\" in order to compress with bzip2: %s", entry_obj->ent.entry->filename, entry_obj->ent.entry->phar->fname, error);
                                        efree(error);
index d8341b78307fd5a83fbaed9fc606132b5769632d..ac879d3cd88a874d16d1471e0edd79cd9b510067 100644 (file)
@@ -618,10 +618,10 @@ static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags,
                                        if (SUCCESS != zend_hash_find(&phar->manifest, key, keylen, (void **) &entry)) {
                                                goto free_resource;
                                        }
-                                       if (!entry->link || !entry->is_mounted) {
+                                       if (!entry->tmp || !entry->is_mounted) {
                                                goto free_resource;
                                        }
-                                       test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->link, internal_file + keylen);
+                                       test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen);
                                        if (SUCCESS != php_stream_stat_path(test, &ssbi)) {
                                                efree(test);
                                                continue;
@@ -844,7 +844,7 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
                entry->is_deleted = 1;
                entry->fp = NULL;
                entry->metadata = 0;
-               entry->link = NULL;
+               entry->link = entry->tmp = NULL;
                source = entry;
 
                /* add to the manifest, and then store the pointer to the new guy in entry */
index ca13e5ba1beff0a8b9ac7f4361d447fefc603630..b4cb09d05cc91075d27a8e92357b317371523692 100644 (file)
@@ -260,7 +260,7 @@ int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, i
                if (entry.tar_type == TAR_LINK) {
                        if (!zend_hash_exists(&myphar->manifest, hdr->linkname, strlen(hdr->linkname))) {
                                if (error) {
-                                       spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file", fname);
+                                       spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%s\"", fname, hdr->linkname);
                                }
                                efree(entry.filename);
                                php_stream_close(fp);
@@ -436,25 +436,27 @@ int phar_tar_writeheaders(void *pDest, void *argument TSRMLS_DC)
        pos = php_stream_tell(fp->new); /* save start of file within tar */
 
        /* write contents */
-       if (FAILURE == phar_open_entry_fp(entry, fp->error TSRMLS_CC)) {
-               return ZEND_HASH_APPLY_STOP;
-       }
-       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
-               if (fp->error) {
-                       spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename);
+       if (entry->uncompressed_filesize) {
+               if (FAILURE == phar_open_entry_fp(entry, fp->error, 0 TSRMLS_CC)) {
+                       return ZEND_HASH_APPLY_STOP;
                }
-               return ZEND_HASH_APPLY_STOP;
-       }
-       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry TSRMLS_CC), fp->new, entry->uncompressed_filesize)) {
-               if (fp->error) {
-                       spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
+                       if (fp->error) {
+                               spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename);
+                       }
+                       return ZEND_HASH_APPLY_STOP;
                }
-               return ZEND_HASH_APPLY_STOP;
+               if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), fp->new, entry->uncompressed_filesize)) {
+                       if (fp->error) {
+                               spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
+                       }
+                       return ZEND_HASH_APPLY_STOP;
+               }
+       
+               memset(padding, 0, 512);
+               php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
        }
 
-       memset(padding, 0, 512);
-       php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
-
        entry->is_modified = 0;
        if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
                if (!entry->fp_refcount) {
index d8b8bc20ac2924f4f5be805ea5ecd39e3bc31df3..b0eba6cc69a22240baa9b05cd2f1c5d60e04c010 100644 (file)
@@ -56,16 +56,20 @@ class corrupt_tarmaker
         }
 
         $link = null;
-        if ($stat['mode'] & 0x4000) {
-            $type = 5;        // Directory
-        } else if ($stat['mode'] & 0x8000) {
-            $type = 0;        // Regular
-        } else if ($stat['mode'] & 0xA000 && $corrupt = 'symlink') {
+        if ($stat['mode'] & 0xA000 && $corrupt === 'symlink') {
             $type = 2;        // Symbolic Link
-            $link = 'file1.txt';
+            $link = $fileOrStream;
+            $stat['size'] = 0;
+            $fileOrStream = '';
         } else if ($stat['mode'] & 0xA000) {
             $type = 1;        // Link
-            $link = 'file1.txt';
+            $link = $fileOrStream;
+            $stat['size'] = 0;
+            $fileOrStream = '';
+        } else if ($stat['mode'] & 0x4000) {
+            $type = 5;        // Directory
+        } else if ($stat['mode'] & 0x8000) {
+            $type = 0;        // Regular
         } else {
             $type = 9;        // Unknown
         }
@@ -108,7 +112,7 @@ class corrupt_tarmaker
         $checksum = 256; // 8 * ord(' ');
         $checksum += array_reduce($checkheader, '_pear2tarchecksum');
 
-       if ($corrupt == 'checksum') $checksum++;
+       if ($corrupt === 'checksum') $checksum++;
         $checksum = pack('a8', sprintf('%6s ', decoct($checksum)));
 
         fwrite($this->tmp, $block . $checksum . $blockend, 512);
@@ -119,7 +123,7 @@ class corrupt_tarmaker
             }
         } else {
             fwrite($this->tmp, $fileOrStream);
-            if (strlen($fileOrStream) % 512) {
+            if (strlen($fileOrStream) && !isset($link) && strlen($fileOrStream) % 512) {
                 fwrite($this->tmp, str_repeat("\0", 512 - strlen($fileOrStream) % 512));
             }
         }
index 2e0fdcbf65c295552a16ef2023ff25676740735e..e67efab5adf320832f966df8b053b5b84d162e4d 100644 (file)
@@ -18,8 +18,11 @@ var_dump($p['testit/file']->getContent());
 <?php
 unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.tar');
 ?>
---EXPECTF--
-string(2) "hi"
-string(2) "hi"
-string(2) "hi"
+--EXPECT--
+string(3) "hi
+"
+string(3) "hi
+"
+string(3) "hi
+"
 ===DONE===
index 1099464fce73ba2845a4521049d9441ae994bcc7..ba10c960ea7d075d8554f78b146e56e289303564 100644 (file)
 extern php_stream_wrapper php_stream_phar_wrapper;
 #endif
 
+/* for links to relative location, prepend cwd of the entry */
+static char *phar_get_link_location(phar_entry_info *entry TSRMLS_DC)
+{
+       char *tmp, *ret, *p;
+       if (!entry->link) {
+               return NULL;
+       }
+       if (entry->link[0] == '/') {
+               return entry->link;
+       }
+       tmp = estrndup(entry->filename, entry->filename_len);
+       p = strrchr(tmp, '/');
+       if (p) {
+               *p = '\0';
+               spprintf(&ret, 0, "%s/%s", tmp, entry->link);
+               efree(tmp);
+               return ret;
+       }
+       efree(ret);
+       return entry->link;
+}
+
+phar_entry_info *phar_get_link_source(phar_entry_info *entry TSRMLS_DC)
+{
+       phar_entry_info *link_entry;
+       char *link = phar_get_link_location(entry);
+
+       if (!entry->link) {
+               return entry;
+       }
+
+       if (SUCCESS == zend_hash_find(&(entry->phar->manifest), entry->link, strlen(entry->link), (void **)&link_entry) ||
+               SUCCESS == zend_hash_find(&(entry->phar->manifest), link, strlen(link), (void **)&link_entry)) {
+               if (link != entry->link) {
+                       efree(link);
+               }
+               return phar_get_link_source(link_entry TSRMLS_CC);
+       } else {
+               if (link != entry->link) {
+                       efree(link);
+               }
+               return NULL;
+       }
+}
+
 /* retrieve a phar_entry_info's current file pointer for reading contents */
-php_stream *phar_get_efp(phar_entry_info *entry TSRMLS_DC)
+php_stream *phar_get_efp(phar_entry_info *entry, int follow_links TSRMLS_DC)
 {
+       if (follow_links && entry->link) {
+               phar_entry_info *link_entry = phar_get_link_source(entry TSRMLS_CC);
+
+               if (link_entry && link_entry != entry) {
+                       return phar_get_efp(link_entry, 1 TSRMLS_CC);
+               }
+       }
        if (entry->fp_type == PHAR_FP) {
                if (!entry->phar->fp) {
                        /* re-open just in time for cases where our refcount reached 0 on the phar archive */
@@ -41,17 +93,24 @@ php_stream *phar_get_efp(phar_entry_info *entry TSRMLS_DC)
        } else {
                /* temporary manifest entry */
                if (!entry->fp) {
-                       entry->fp = php_stream_open_wrapper(entry->link, "rb", STREAM_MUST_SEEK|0, NULL);
+                       entry->fp = php_stream_open_wrapper(entry->tmp, "rb", STREAM_MUST_SEEK|0, NULL);
                }
                return entry->fp;
        }
 }
 
-int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position TSRMLS_DC)
+int phar_seek_efp(phar_entry_info *entry, off_t offset, int whence, off_t position, int follow_links TSRMLS_DC)
 {
-       php_stream *fp = phar_get_efp(entry TSRMLS_CC);
+       php_stream *fp = phar_get_efp(entry, follow_links TSRMLS_CC);
        off_t temp;
 
+       if (follow_links) {
+               phar_entry_info *t;
+               t = phar_get_link_source(entry TSRMLS_CC);
+               if (t) {
+                       entry = t;
+               }
+       }
        if (entry->is_dir) {
                return 0;
        }
@@ -94,26 +153,26 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
 #endif
        entry.filename_len = path_len;
        if (strstr(filename, "phar://")) {
-               entry.link = estrndup(filename, filename_len);
+               entry.tmp = estrndup(filename, filename_len);
        } else {
-               entry.link = expand_filepath(filename, NULL TSRMLS_CC);
-               if (!entry.link) {
-                       entry.link = estrndup(filename, filename_len);
+               entry.tmp = expand_filepath(filename, NULL TSRMLS_CC);
+               if (!entry.tmp) {
+                       entry.tmp = estrndup(filename, filename_len);
                }
        }
 #if PHP_MAJOR_VERSION < 6
-       if (PG(safe_mode) && !strstr(filename, "phar://") && (!php_checkuid(entry.link, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
-               efree(entry.link);
+       if (PG(safe_mode) && !strstr(filename, "phar://") && (!php_checkuid(entry.tmp, NULL, CHECKUID_ALLOW_ONLY_FILE))) {
+               efree(entry.tmp);
                efree(entry.filename);
                return FAILURE;
        }
 #endif
 
-       filename_len = strlen(entry.link);
-       filename = entry.link;
+       filename_len = strlen(entry.tmp);
+       filename = entry.tmp;
        /* only check openbasedir for files, not for phar streams */
        if (!strstr(filename, "phar://") && php_check_open_basedir(filename TSRMLS_CC)) {
-               efree(entry.link);
+               efree(entry.tmp);
                efree(entry.filename);
                return FAILURE;
        }
@@ -122,7 +181,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
        entry.fp_type = PHAR_TMP;
 
        if (SUCCESS != php_stream_stat_path(filename, &ssb)) {
-               efree(entry.link);
+               efree(entry.tmp);
                efree(entry.filename);
                return FAILURE;
        }
@@ -130,7 +189,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
                entry.is_dir = 1;
                if (SUCCESS != zend_hash_add(&phar->mounted_dirs, entry.filename, path_len, (void *)&(entry.filename), sizeof(char *), NULL)) {
                        /* directory already mounted */
-                       efree(entry.link);
+                       efree(entry.tmp);
                        efree(entry.filename);
                        return FAILURE;
                }
@@ -142,7 +201,7 @@ int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len,
        if (SUCCESS == zend_hash_add(&phar->manifest, entry.filename, path_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
                return SUCCESS;
        }
-       efree(entry.link);
+       efree(entry.tmp);
        efree(entry.filename);
        return FAILURE;
 }
@@ -487,9 +546,14 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char
                                return FAILURE;
                        }
                } else if (for_append) {
-                       phar_seek_efp(entry, 0, SEEK_END, 0 TSRMLS_CC);
+                       phar_seek_efp(entry, 0, SEEK_END, 0, 0 TSRMLS_CC);
                }
        } else {
+               if (entry->link) {
+                       efree(entry->link);
+                       entry->link = NULL;
+                       entry->tar_type = (entry->tar_type ? TAR_FILE : 0);
+               }
                if (for_write) {
                        if (for_trunc) {
                                if (FAILURE == phar_create_writeable_entry(phar, entry, error TSRMLS_CC)) {
@@ -501,7 +565,7 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char
                                }
                        }
                } else {
-                       if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+                       if (FAILURE == phar_open_entry_fp(entry, error, 1 TSRMLS_CC)) {
                                return FAILURE;
                        }
                }
@@ -513,7 +577,7 @@ int phar_get_entry_data(phar_entry_data **ret, char *fname, int fname_len, char
        (*ret)->internal_file = entry;
        (*ret)->is_zip = entry->is_zip;
        (*ret)->is_tar = entry->is_tar;
-       (*ret)->fp = phar_get_efp(entry TSRMLS_CC);
+       (*ret)->fp = phar_get_efp(entry, 1 TSRMLS_CC);
        (*ret)->zero = entry->offset;
        ++(entry->fp_refcount);
        ++(entry->phar->refcount);
@@ -637,16 +701,27 @@ int phar_open_archive_fp(phar_archive_data *phar TSRMLS_DC)
 /* copy file data from an existing to a new phar_entry_info that is not in the manifest */
 int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **error TSRMLS_DC)
 {
-       if (FAILURE == phar_open_entry_fp(source, error TSRMLS_CC)) {
+       phar_entry_info *link;
+
+       if (FAILURE == phar_open_entry_fp(source, error, 1 TSRMLS_CC)) {
                return FAILURE;
        }
+       if (dest->link) {
+               efree(dest->link);
+               dest->link = NULL;
+               dest->tar_type = (dest->tar_type ? TAR_FILE : 0);
+       }
        dest->fp_type = PHAR_MOD;
        dest->offset = 0;
        dest->is_modified = 1;
        dest->fp = php_stream_fopen_tmpfile();
 
-       phar_seek_efp(source, 0, SEEK_SET, 0 TSRMLS_CC);
-       if (source->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(source TSRMLS_CC), dest->fp, source->uncompressed_filesize)) {
+       phar_seek_efp(source, 0, SEEK_SET, 0, 1 TSRMLS_CC);
+       link = phar_get_link_source(source TSRMLS_CC);
+       if (!link) {
+               link = source;
+       }
+       if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), dest->fp, link->uncompressed_filesize)) {
                php_stream_close(dest->fp);
                dest->fp_type = PHAR_FP;
                if (error) {
@@ -659,16 +734,23 @@ int phar_copy_entry_fp(phar_entry_info *source, phar_entry_info *dest, char **er
 
 /* open and decompress a compressed phar entry
  */
-int phar_open_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC) 
+int phar_open_entry_fp(phar_entry_info *entry, char **error, int follow_links TSRMLS_DC) 
 {
        php_stream_filter *filter;
        phar_archive_data *phar = entry->phar;
        char *filtername;
        off_t loc;
 
+       if (follow_links && entry->link) {
+               phar_entry_info *link_entry = phar_get_link_source(entry TSRMLS_CC);
+
+               if (link_entry && link_entry != entry) {
+                       return phar_open_entry_fp(link_entry, error, 1 TSRMLS_CC);
+               }
+       }
        if (entry->fp_type == PHAR_TMP) {
                if (!entry->fp) {
-                       entry->fp = php_stream_open_wrapper(entry->link, "rb", STREAM_MUST_SEEK|0, NULL);
+                       entry->fp = php_stream_open_wrapper(entry->tmp, "rb", STREAM_MUST_SEEK|0, NULL);
                }
                return SUCCESS;
        }
@@ -786,6 +868,11 @@ int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry,
                *error = NULL;
        }
        /* open a new temp file for writing */
+       if (entry->link) {
+               efree(entry->link);
+               entry->link = NULL;
+               entry->tar_type = (entry->tar_type ? TAR_FILE : 0);
+       }
        entry->fp = php_stream_fopen_tmpfile();
        if (!entry->fp) {
                if (error) {
@@ -810,8 +897,9 @@ int phar_create_writeable_entry(phar_archive_data *phar, phar_entry_info *entry,
 int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC)
 {
        php_stream *fp;
+       phar_entry_info *link;
 
-       if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+       if (FAILURE == phar_open_entry_fp(entry, error, 1 TSRMLS_CC)) {
                return FAILURE;
        }
 
@@ -820,14 +908,24 @@ int phar_separate_entry_fp(phar_entry_info *entry, char **error TSRMLS_DC)
        }
 
        fp = php_stream_fopen_tmpfile();
-       phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
-       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry TSRMLS_CC), fp, entry->uncompressed_filesize)) {
+       phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC);
+       link = phar_get_link_source(entry TSRMLS_CC);
+       if (!link) {
+               link = entry;
+       }
+       if (link->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(link, 0 TSRMLS_CC), fp, link->uncompressed_filesize)) {
                if (error) {
                        spprintf(error, 4096, "phar error: cannot separate entry file \"%s\" contents in phar archive \"%s\" for write access", entry->filename, entry->phar->fname);
                }
                return FAILURE;
        }
 
+       if (entry->link) {
+               efree(entry->link);
+               entry->link = NULL;
+               entry->tar_type = (entry->tar_type ? TAR_FILE : 0);
+       }
+
        entry->offset = 0;
        entry->fp = fp;
        entry->fp_type = PHAR_MOD;
@@ -845,10 +943,10 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry,
                *error = NULL;
        }
        /* seek to start of internal file and read it */
-       if (FAILURE == phar_open_entry_fp(entry, error TSRMLS_CC)) {
+       if (FAILURE == phar_open_entry_fp(entry, error, 1 TSRMLS_CC)) {
                return NULL;
        }
-       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+       if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 1 TSRMLS_CC)) {
                spprintf(error, 4096, "phar error: cannot seek to start of file \"%s\" in phar \"%s\"", entry->filename, phar->fname);
                return NULL;
        }
@@ -1050,13 +1148,13 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in
                                        }
                                        return NULL;
                                }
-                               if (!entry->link || !entry->is_mounted) {
+                               if (!entry->tmp || !entry->is_mounted) {
                                        if (error) {
                                                spprintf(error, 4096, "phar internal error: mounted path \"%s\" is not properly initialized as a mounted path", key);
                                        }
                                        return NULL;
                                }
-                               test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->link, path + keylen);
+                               test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, path + keylen);
                                if (SUCCESS != php_stream_stat_path(test, &ssb)) {
                                        efree(test);
                                        return NULL;
index cd35b874fa8f9417f150fd52dd7cd0e988766358..886cb20c5da2d71b3c0fc6ac528cf4535b17f28d 100644 (file)
@@ -539,15 +539,15 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
                        }
                        goto continue_dir;
                }
-               if (FAILURE == phar_open_entry_fp(entry, p->error TSRMLS_CC)) {
+               if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
                        spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                        return ZEND_HASH_APPLY_STOP;
                }
-               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
                        spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                        return ZEND_HASH_APPLY_STOP;
                }
-               efp = phar_get_efp(entry TSRMLS_CC);
+               efp = phar_get_efp(entry, 0 TSRMLS_CC);
 
                newcrc32 = ~0;
                for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
@@ -580,7 +580,7 @@ static int phar_zip_changed_apply(void *data, void *arg TSRMLS_DC) /* {{{ */
                        return ZEND_HASH_APPLY_STOP;
                }
                php_stream_flush(efp);
-               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC)) {
+               if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC)) {
                        spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                        return ZEND_HASH_APPLY_STOP;
                }
@@ -676,11 +676,11 @@ continue_dir:
                        php_stream_close(entry->cfp);
                        entry->cfp = NULL;
                } else {
-                       if (FAILURE == phar_open_entry_fp(entry, p->error TSRMLS_CC)) {
+                       if (FAILURE == phar_open_entry_fp(entry, p->error, 0 TSRMLS_CC)) {
                                return ZEND_HASH_APPLY_STOP;
                        }
-                       phar_seek_efp(entry, 0, SEEK_SET, 0 TSRMLS_CC);
-                       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry TSRMLS_CC), p->filefp, entry->uncompressed_filesize)) {
+                       phar_seek_efp(entry, 0, SEEK_SET, 0, 0 TSRMLS_CC);
+                       if (entry->uncompressed_filesize != php_stream_copy_to_stream(phar_get_efp(entry, 0 TSRMLS_CC), p->filefp, entry->uncompressed_filesize)) {
                                spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
                                return ZEND_HASH_APPLY_STOP;
                        }