Skip to content

Commit 18c560d

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix bug #74154: Phar extractTo creates empty files
2 parents 898c945 + 27d4e2f commit 18c560d

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

ext/phar/phar.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -260,20 +260,26 @@ bool phar_archive_delref(phar_archive_data *phar) /* {{{ */
260260
PHAR_G(last_phar) = NULL;
261261
PHAR_G(last_phar_name) = PHAR_G(last_alias) = NULL;
262262

263+
/* This is a new phar that has perhaps had an alias/metadata set, but has never been flushed. */
264+
bool remove_fname_cache = !zend_hash_num_elements(&phar->manifest);
265+
263266
if (phar->fp && (!(phar->flags & PHAR_FILE_COMPRESSION_MASK) || !phar->alias)) {
264267
/* close open file handle - allows removal or rename of
265268
the file on windows, which has greedy locking
266-
only close if the archive was not already compressed. If it
267-
was compressed, then the fp does not refer to the original file.
268-
We're also closing compressed files to save resources,
269-
but only if the archive isn't aliased. */
269+
only close if the archive was not already compressed.
270+
We're also closing compressed files to save resources, but only if the archive isn't aliased.
271+
If it was compressed, then the fp does not refer to the original compressed file:
272+
it refers to the **uncompressed** filtered file stream.
273+
Therefore, upon closing a compressed file we need to invalidate the phar archive such
274+
that the code that reopens the phar will not try to use the **compressed** file as if it was uncompressed.
275+
That would result in treating compressed file data as if it were compressed and using uncompressed file offsets
276+
on the compressed file. */
270277
php_stream_close(phar->fp);
271278
phar->fp = NULL;
279+
remove_fname_cache |= phar->flags & PHAR_FILE_COMPRESSION_MASK;
272280
}
273281

274-
if (!zend_hash_num_elements(&phar->manifest)) {
275-
/* this is a new phar that has perhaps had an alias/metadata set, but has never
276-
been flushed */
282+
if (remove_fname_cache) {
277283
if (zend_hash_str_del(&(PHAR_G(phar_fname_map)), phar->fname, phar->fname_len) != SUCCESS) {
278284
phar_destroy_phar_data(phar);
279285
}

ext/phar/tests/bug74154.phpt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Bug #74154 (Phar extractTo creates empty files)
3+
--EXTENSIONS--
4+
phar
5+
--FILE--
6+
<?php
7+
8+
$dir = __DIR__.'/bug74154';
9+
mkdir($dir);
10+
file_put_contents("$dir/1.txt", str_repeat('h', 64));
11+
file_put_contents("$dir/2.txt", str_repeat('i', 64));
12+
$phar = new PharData(__DIR__.'/bug74154.tar');
13+
$phar->buildFromDirectory($dir);
14+
15+
$compPhar = $phar->compress(Phar::GZ);
16+
unset($phar); //make sure that test.tar is closed
17+
unlink(__DIR__.'/bug74154.tar');
18+
unset($compPhar); //make sure that test.tar.gz is closed
19+
$extractingPhar = new PharData(__DIR__.'/bug74154.tar.gz');
20+
$extractingPhar->extractTo($dir.'_out');
21+
22+
var_dump(file_get_contents($dir.'_out/1.txt'));
23+
var_dump(file_get_contents($dir.'_out/2.txt'));
24+
25+
?>
26+
--CLEAN--
27+
<?php
28+
$dir = __DIR__.'/bug74154';
29+
@unlink("$dir/1.txt");
30+
@unlink("$dir/2.txt");
31+
@rmdir($dir);
32+
@unlink($dir.'_out/1.txt');
33+
@unlink($dir.'_out/2.txt');
34+
@rmdir($dir.'_out');
35+
@unlink(__DIR__.'/bug74154.tar');
36+
@unlink(__DIR__.'/bug74154.tar.gz');
37+
?>
38+
--EXPECT--
39+
string(64) "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
40+
string(64) "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"

ext/phar/tests/tar/bug70417.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ $filename = __DIR__ . '/bug70417.tar';
1010
$resBefore = count(get_resources());
1111
$arch = new PharData($filename);
1212
$arch->addFromString('foo', 'bar');
13+
$arch->addFromString('foo2', 'baz');
1314
$arch->compress(Phar::GZ);
1415
unset($arch);
1516
$resAfter = count(get_resources());
16-
var_dump($resBefore === $resAfter);
17+
var_dump($resAfter - $resBefore);
1718
?>
1819
--CLEAN--
1920
<?php
@@ -22,4 +23,4 @@ $filename = __DIR__ . '/bug70417.tar';
2223
@unlink("$filename.gz");
2324
?>
2425
--EXPECT--
25-
bool(true)
26+
int(0)

0 commit comments

Comments
 (0)