From: Greg Beaver Date: Mon, 21 Apr 2008 06:17:51 +0000 (+0000) Subject: implement symbolic link support within a tar-based phar archive X-Git-Tag: RELEASE_2_0_0b1~295 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3d858f4aa9b0eb47ad015b4d13b447baa836cf6f;p=php implement symbolic link support within a tar-based phar archive 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. --- diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index 7bc3a80bc3..1d372829fd 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -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); diff --git a/ext/phar/phar.c b/ext/phar/phar.c index b429b3aab7..7b5e3e3229 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -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); } diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index b6f6bf49d6..aab5b3fd02 100755 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -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); diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index a1b01a4a17..d486a2888b 100755 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -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); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index d8341b7830..ac879d3cd8 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -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 */ diff --git a/ext/phar/tar.c b/ext/phar/tar.c index ca13e5ba1b..b4cb09d05c 100644 --- a/ext/phar/tar.c +++ b/ext/phar/tar.c @@ -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) { diff --git a/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc b/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc index d8b8bc20ac..b0eba6cc69 100644 --- a/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc +++ b/ext/phar/tests/tar/files/corrupt_tarmaker.php.inc @@ -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)); } } diff --git a/ext/phar/tests/tar/links.phpt b/ext/phar/tests/tar/links.phpt index 2e0fdcbf65..e67efab5ad 100644 --- a/ext/phar/tests/tar/links.phpt +++ b/ext/phar/tests/tar/links.phpt @@ -18,8 +18,11 @@ var_dump($p['testit/file']->getContent()); ---EXPECTF-- -string(2) "hi" -string(2) "hi" -string(2) "hi" +--EXPECT-- +string(3) "hi +" +string(3) "hi +" +string(3) "hi +" ===DONE=== diff --git a/ext/phar/util.c b/ext/phar/util.c index 1099464fce..ba10c960ea 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -25,9 +25,61 @@ 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; diff --git a/ext/phar/zip.c b/ext/phar/zip.c index cd35b874fa..886cb20c5d 100644 --- a/ext/phar/zip.c +++ b/ext/phar/zip.c @@ -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; }