1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix bug #72666: stat cache not cleared for plain paths

This adds more aggressive clearing of stat cache. It is added to the
filestat as well as plain wrapper operations which covers stream file
accessing as well as exec functions (using pipes). It should hopefully
fix the most visible issues with the stat cache.

Closes GH-17681
This commit is contained in:
Jakub Zelenka
2025-01-25 23:08:27 +01:00
parent 3b4a58da44
commit 930624899b
7 changed files with 158 additions and 4 deletions

4
NEWS
View File

@@ -62,6 +62,10 @@ PHP NEWS
. Fixed bug GH-15902 (Core dumped in ext/reflection/php_reflection.c).
(DanielEScherzer)
- Standard:
. Fixed bug #72666 (stat cache clearing inconsistent between file:// paths
and plain paths). (Jakub Zelenka)
- Streams:
. Fixed bug GH-17650 (realloc with size 0 in user_filters.c). (nielsdos)
. Fix memory leak on overflow in _php_stream_scandir(). (nielsdos)

View File

@@ -388,6 +388,9 @@ static void php_do_chgrp(INTERNAL_FUNCTION_PARAMETERS, int do_lchgrp) /* {{{ */
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
php_clear_stat_cache(0, NULL, 0);
RETURN_TRUE;
#endif
}
@@ -527,6 +530,9 @@ static void php_do_chown(INTERNAL_FUNCTION_PARAMETERS, int do_lchown) /* {{{ */
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
php_clear_stat_cache(0, NULL, 0);
RETURN_TRUE;
#endif
}
@@ -591,6 +597,9 @@ PHP_FUNCTION(chmod)
php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
php_clear_stat_cache(0, NULL, 0);
RETURN_TRUE;
}
/* }}} */
@@ -676,6 +685,9 @@ PHP_FUNCTION(touch)
php_error_docref(NULL, E_WARNING, "Utime failed: %s", strerror(errno));
RETURN_FALSE;
}
php_clear_stat_cache(0, NULL, 0);
RETURN_TRUE;
}
/* }}} */

View File

@@ -0,0 +1,19 @@
--TEST--
Bug #72666 (stat cache clearing inconsistent - touch)
--FILE--
<?php
$filename = __DIR__ . '/bug72666_variation1.txt';
touch($filename);
var_dump(filemtime($filename) > 2);
touch($filename, 1);
var_dump(filemtime($filename));
?>
--CLEAN--
<?php
unlink(__DIR__ . '/bug72666_variation1.txt');
?>
--EXPECT--
bool(true)
int(1)

View File

@@ -0,0 +1,35 @@
--TEST--
Bug #72666 (stat cache clearing inconsistent - chgrp, chmod)
--SKIPIF--
<?php
if (substr(PHP_OS, 0, 3) == 'WIN') die('skip no windows support');
if (!function_exists("posix_getgid")) die("skip no posix_getgid()");
?>
--FILE--
<?php
$filename = __DIR__ . '/bug72666_variation2.txt';
$gid = posix_getgid();
var_dump(touch($filename));
$ctime1 = filectime($filename);
sleep(1);
var_dump(chgrp($filename,$gid));
$ctime2 = filectime($filename);
sleep(1);
var_dump(chmod($filename, 0777));
$ctime3 = filectime($filename);
var_dump($ctime2 > $ctime1);
var_dump($ctime3 > $ctime2);
?>
--CLEAN--
<?php
unlink(__DIR__ . '/bug72666_variation2.txt');
?>
--EXPECT--
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)

View File

@@ -0,0 +1,35 @@
--TEST--
Bug #72666 (stat cache clearing inconsistent - plain wrapper)
--FILE--
<?php
$filename = __DIR__ . '/bug72666_variation3.txt';
file_put_contents($filename, "test");
$fd = fopen($filename, "r");
$atime1 = fileatime($filename);
sleep(1);
var_dump(fread($fd, 4));
$atime2 = fileatime($filename);
$mtime1 = filemtime($filename);
fclose($fd);
$fd = fopen($filename, "w");
sleep(1);
var_dump(fwrite($fd, "data"));
$mtime2 = filemtime($filename);
if (substr(PHP_OS, 0, 3) == 'WIN') {
// Windows do not hundle atime
var_dump($atime2 == $atime1);
} else {
var_dump($atime2 > $atime1);
}
var_dump($mtime2 > $mtime1);
?>
--CLEAN--
<?php
unlink(__DIR__ . '/bug72666_variation3.txt');
?>
--EXPECT--
string(4) "test"
int(4)
bool(true)
bool(true)

View File

@@ -0,0 +1,26 @@
--TEST--
Bug #72666 (stat cache clearing inconsistent - exec)
--FILE--
<?php
$filename = __DIR__ . '/bug72666_variation4.txt';
touch($filename, 1);
var_dump(filemtime($filename));
exec("touch $filename");
var_dump(filemtime($filename) > 1);
touch($filename, 1);
var_dump(filemtime($filename));
shell_exec("touch $filename");
var_dump(filemtime($filename) > 1);
?>
--CLEAN--
<?php
unlink(__DIR__ . '/bug72666_variation4.txt');
?>
--EXPECT--
int(1)
bool(true)
int(1)
bool(true)

View File

@@ -349,14 +349,15 @@ PHPAPI php_stream *_php_stream_fopen_from_pipe(FILE *file, const char *mode STRE
static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t count)
{
php_stdio_stream_data *data = (php_stdio_stream_data*)stream->abstract;
ssize_t bytes_written;
assert(data != NULL);
if (data->fd >= 0) {
#ifdef PHP_WIN32
ssize_t bytes_written = _write(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));
bytes_written = _write(data->fd, buf, PLAIN_WRAP_BUF_SIZE(count));
#else
ssize_t bytes_written = write(data->fd, buf, count);
bytes_written = write(data->fd, buf, count);
#endif
if (bytes_written < 0) {
if (PHP_IS_TRANSIENT_ERROR(errno)) {
@@ -370,7 +371,6 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun
php_error_docref(NULL, E_NOTICE, "Write of %zu bytes failed with errno=%d %s", count, errno, strerror(errno));
}
}
return bytes_written;
} else {
#ifdef HAVE_FLUSHIO
@@ -380,8 +380,15 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun
data->last_op = 'w';
#endif
return (ssize_t) fwrite(buf, 1, count, data->file);
bytes_written = (ssize_t) fwrite(buf, 1, count, data->file);
}
if (EG(active)) {
/* clear stat cache as mtime and ctime got changed */
php_clear_stat_cache(0, NULL, 0);
}
return bytes_written;
}
static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
@@ -460,6 +467,12 @@ static ssize_t php_stdiop_read(php_stream *stream, char *buf, size_t count)
stream->eof = feof(data->file);
}
if (EG(active)) {
/* clear stat cache as atime got changed */
php_clear_stat_cache(0, NULL, 0);
}
return ret;
}
@@ -540,6 +553,10 @@ static int php_stdiop_flush(php_stream *stream)
* something completely different.
*/
if (data->file) {
if (EG(active)) {
/* clear stat cache as there might be a write so mtime and ctime might have changed */
php_clear_stat_cache(0, NULL, 0);
}
return fflush(data->file);
}
return 0;
@@ -1154,6 +1171,12 @@ PHPAPI php_stream *_php_stream_fopen(const char *filename, const char *mode, zen
ret = php_stream_fopen_from_fd_rel(fd, mode, persistent_id, (open_flags & O_APPEND) == 0);
}
if (EG(active)) {
/* clear stat cache as mtime and ctime might got changed - phar can use stream before
* cache is initialized so we need to check if the execution is active. */
php_clear_stat_cache(0, NULL, 0);
}
if (ret) {
if (opened_path) {
*opened_path = zend_string_init(realpath, strlen(realpath), 0);