diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 9687c71e989..3cc50972342 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -46,6 +46,7 @@ #include "zend_accelerator_util_funcs.h" #include "zend_accelerator_hash.h" #include "zend_file_cache.h" +#include "zend_system_id.h" #include "ext/pcre/php_pcre.h" #include "ext/standard/basic_functions.h" @@ -1411,9 +1412,6 @@ zend_result zend_accel_invalidate(zend_string *filename, bool force) } if (ZCG(accel_directives).file_cache) { - if (ZCG(accel_directives).file_cache_read_only) { - return FAILURE; - } zend_file_cache_invalidate(realpath); } @@ -3306,33 +3304,48 @@ static zend_result accel_post_startup(void) } /* opcache.file_cache_read_only should only be enabled when all script files are read-only */ + int file_cache_access_mode = 0; + if (ZCG(accel_directives).file_cache_read_only) { + zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache is in read-only mode"); + if (!ZCG(accel_directives).file_cache) { accel_startup_ok = false; zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_read_only is set without a proper setting of opcache.file_cache"); return SUCCESS; } - if (ZCG(accel_directives).revalidate_freq != 0) { - accel_startup_ok = false; - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_read_only cannot be enabled when opcache.revalidate_freq is not 0."); - return SUCCESS; - } - if (ZCG(accel_directives).validate_timestamps) { - accel_startup_ok = false; - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_read_only cannot be enabled when opcache.validate_timestamps is enabled."); - return SUCCESS; - } + + /* opcache.file_cache is read only, so ensure the directory is readable */ +#ifndef ZEND_WIN32 + file_cache_access_mode = R_OK | X_OK; +#else + file_cache_access_mode = 04; // Read access +#endif } else { /* opcache.file_cache isn't read only, so ensure the directory is writable */ - if ( ZCG(accel_directives).file_cache && #ifndef ZEND_WIN32 - access(ZCG(accel_directives).file_cache, R_OK | W_OK | X_OK) != 0 + file_cache_access_mode = R_OK | W_OK | X_OK; #else - _access(ZCG(accel_directives).file_cache, 06) != 0 + file_cache_access_mode = 06; // Read and write access #endif - ) { + } + + if ( ZCG(accel_directives).file_cache ) { + zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache running with PHP build ID: %s", zend_system_id); + + zend_stat_t buf = {0}; + + if (!IS_ABSOLUTE_PATH(ZCG(accel_directives).file_cache, strlen(ZCG(accel_directives).file_cache)) || + zend_stat(ZCG(accel_directives).file_cache, &buf) != 0 || + !S_ISDIR(buf.st_mode) || +#ifndef ZEND_WIN32 + access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0 +#else + _access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0 +#endif + ) { accel_startup_ok = false; - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache must be a full path of an accessible, writable directory"); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache must be a full path of an accessible directory"); return SUCCESS; } } diff --git a/ext/opcache/tests/zzz_basic_logging.phpt b/ext/opcache/tests/zzz_basic_logging.phpt index 7490fadc62e..a53c6a8db9d 100644 --- a/ext/opcache/tests/zzz_basic_logging.phpt +++ b/ext/opcache/tests/zzz_basic_logging.phpt @@ -6,6 +6,7 @@ outputs the correct logging at the highest log_verbosity_level --INI-- opcache.enable=1 opcache.enable_cli=1 +opcache.file_cache= opcache.file_cache_only=0 opcache.error_log= opcache.log_verbosity_level=4 diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index f86a7e2aebb..1b62b757b87 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -165,20 +165,6 @@ static ZEND_INI_MH(OnUpdateFileCache) if (new_value) { if (!ZSTR_LEN(new_value)) { new_value = NULL; - } else { - zend_stat_t buf = {0}; - - if (!IS_ABSOLUTE_PATH(ZSTR_VAL(new_value), ZSTR_LEN(new_value)) || - zend_stat(ZSTR_VAL(new_value), &buf) != 0 || - !S_ISDIR(buf.st_mode) || -#ifndef ZEND_WIN32 - access(ZSTR_VAL(new_value), R_OK | X_OK) != 0) { -#else - _access(ZSTR_VAL(new_value), 04) != 0) { -#endif - zend_accel_error(ACCEL_LOG_WARNING, "opcache.file_cache must be a full path of accessible directory.\n"); - new_value = NULL; - } } } OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); @@ -311,10 +297,10 @@ ZEND_INI_BEGIN() STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString, accel_directives.mmap_base, zend_accel_globals, accel_globals) #endif - STD_PHP_INI_ENTRY("opcache.file_cache" , NULL , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache, zend_accel_globals, accel_globals) - STD_PHP_INI_BOOLEAN("opcache.file_cache_read_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_read_only, zend_accel_globals, accel_globals) - STD_PHP_INI_BOOLEAN("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals) - STD_PHP_INI_BOOLEAN("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.file_cache" , NULL , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.file_cache_read_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_read_only, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.file_cache_only" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_only, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.file_cache_consistency_checks" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_consistency_checks, zend_accel_globals, accel_globals) #if ENABLE_FILE_CACHE_FALLBACK STD_PHP_INI_BOOLEAN("opcache.file_cache_fallback" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_cache_fallback, zend_accel_globals, accel_globals) #endif diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index cc22c78d825..fe54c477cea 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -1847,7 +1847,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (info)\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1857,7 +1859,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1865,7 +1869,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1877,7 +1883,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename); } close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } efree(filename); return NULL; } @@ -1895,7 +1903,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (mem)\n", filename); zend_file_cache_flock(fd, LOCK_UN); close(fd); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } zend_arena_release(&CG(arena), checkpoint); efree(filename); return NULL; @@ -1909,7 +1919,9 @@ zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handl if (ZCG(accel_directives).file_cache_consistency_checks && (actual_checksum = zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size)) != info.checksum) { zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s' excepted checksum: 0x%08x actual checksum: 0x%08x\n", filename, info.checksum, actual_checksum); - zend_file_cache_unlink(filename); + if (!ZCG(accel_directives).file_cache_read_only) { + zend_file_cache_unlink(filename); + } zend_arena_release(&CG(arena), checkpoint); efree(filename); return NULL; @@ -2001,6 +2013,10 @@ use_process_mem: void zend_file_cache_invalidate(zend_string *full_path) { + if (ZCG(accel_directives).file_cache_read_only) { + return; + } + char *filename; filename = zend_file_cache_get_bin_file_path(full_path); diff --git a/php.ini-development b/php.ini-development index 5adb11d2156..31353facdb3 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1766,6 +1766,15 @@ ldap.max_links = -1 ; SHM reset. The default "" disables file based caching. ;opcache.file_cache= +; Enables or disables read-only mode for the second level cache directory. +; It should improve performance for read-only containers, +; when the cache is pre-warmed and packaged alongside the application. +; Best used with `opcache.validate_timestamps=0`, `opcache.enable_file_override=1` +; and `opcache.file_cache_consistency_checks=0`. +; Note: A cache generated with a different build of PHP, a different file path, +; or different settings (including which extensions are loaded), may be ignored. +;opcache.file_cache_read_only=0 + ; Enables or disables opcode caching in shared memory. ;opcache.file_cache_only=0 diff --git a/php.ini-production b/php.ini-production index e4ffc1084b1..e4fbd6e785f 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1768,6 +1768,15 @@ ldap.max_links = -1 ; SHM reset. The default "" disables file based caching. ;opcache.file_cache= +; Enables or disables read-only mode for the second level cache directory. +; It should improve performance for read-only containers, +; when the cache is pre-warmed and packaged alongside the application. +; Best used with `opcache.validate_timestamps=0`, `opcache.enable_file_override=1` +; and `opcache.file_cache_consistency_checks=0`. +; Note: A cache generated with a different build of PHP, a different file path, +; or different settings (including which extensions are loaded), may be ignored. +;opcache.file_cache_read_only=0 + ; Enables or disables opcode caching in shared memory. ;opcache.file_cache_only=0