diff --git a/NEWS b/NEWS index aa80c7c6071..ede93bdf118 100644 --- a/NEWS +++ b/NEWS @@ -81,6 +81,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) diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c index 0825fa05864..705370a5906 100644 --- a/ext/standard/filestat.c +++ b/ext/standard/filestat.c @@ -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; } /* }}} */ diff --git a/ext/standard/tests/file/bug72666_variation1.phpt b/ext/standard/tests/file/bug72666_variation1.phpt new file mode 100644 index 00000000000..6e59405d14c --- /dev/null +++ b/ext/standard/tests/file/bug72666_variation1.phpt @@ -0,0 +1,19 @@ +--TEST-- +Bug #72666 (stat cache clearing inconsistent - touch) +--FILE-- + 2); +touch($filename, 1); +var_dump(filemtime($filename)); + +?> +--CLEAN-- + +--EXPECT-- +bool(true) +int(1) diff --git a/ext/standard/tests/file/bug72666_variation2.phpt b/ext/standard/tests/file/bug72666_variation2.phpt new file mode 100644 index 00000000000..7621133c71b --- /dev/null +++ b/ext/standard/tests/file/bug72666_variation2.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug #72666 (stat cache clearing inconsistent - chgrp, chmod) +--SKIPIF-- + +--FILE-- + $ctime1); +var_dump($ctime3 > $ctime2); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/file/bug72666_variation3.phpt b/ext/standard/tests/file/bug72666_variation3.phpt new file mode 100644 index 00000000000..a491640c4f7 --- /dev/null +++ b/ext/standard/tests/file/bug72666_variation3.phpt @@ -0,0 +1,35 @@ +--TEST-- +Bug #72666 (stat cache clearing inconsistent - plain wrapper) +--FILE-- + $atime1); +} +var_dump($mtime2 > $mtime1); +?> +--CLEAN-- + +--EXPECT-- +string(4) "test" +int(4) +bool(true) +bool(true) diff --git a/ext/standard/tests/file/bug72666_variation4.phpt b/ext/standard/tests/file/bug72666_variation4.phpt new file mode 100644 index 00000000000..09e32dafed9 --- /dev/null +++ b/ext/standard/tests/file/bug72666_variation4.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #72666 (stat cache clearing inconsistent - exec) +--FILE-- + 1); + + +touch($filename, 1); +var_dump(filemtime($filename)); +shell_exec("touch $filename"); +var_dump(filemtime($filename) > 1); +?> +--CLEAN-- + +--EXPECT-- +int(1) +bool(true) +int(1) +bool(true) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 7b0813c3db6..85771eaf5ef 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -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);