diff --git a/NEWS b/NEWS index d9709800d8a..2bc7d6131aa 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,7 @@ PHP NEWS - OPcache: . Fixed ZTS OPcache build on Cygwin. (cmb) + . Added opcache.file_cache_read_only. (Samuel Melrose) - Output: . Fixed calculation of aligned buffer size. (cmb) diff --git a/UPGRADING b/UPGRADING index f379c46cb59..9daf6cca226 100644 --- a/UPGRADING +++ b/UPGRADING @@ -143,6 +143,17 @@ PHP 8.5 UPGRADE NOTES 11. Changes to INI File Handling ======================================== +- Opcache: + . Added opcache.file_cache_read_only to support a read-only + opcache.file_cache directory, for use with read-only file systems + (e.g. read-only Docker containers). + 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. + ======================================== 12. Windows Support ======================================== diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index d1be1e7ddcb..9687c71e989 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -1411,6 +1411,9 @@ 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); } @@ -3301,6 +3304,39 @@ static zend_result accel_post_startup(void) #endif accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals)); } + + /* opcache.file_cache_read_only should only be enabled when all script files are read-only */ + if (ZCG(accel_directives).file_cache_read_only) { + 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; + } + } 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 +#else + _access(ZCG(accel_directives).file_cache, 06) != 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"); + return SUCCESS; + } + } + #if ENABLE_FILE_CACHE_FALLBACK file_cache_fallback: #endif diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 162892bf2c2..3f7eb3bdf00 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -177,6 +177,7 @@ typedef struct _zend_accel_directives { char *lockfile_path; #endif char *file_cache; + bool file_cache_read_only; bool file_cache_only; bool file_cache_consistency_checks; #if ENABLE_FILE_CACHE_FALLBACK diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 2ed8155eff7..f86a7e2aebb 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -172,9 +172,9 @@ static ZEND_INI_MH(OnUpdateFileCache) zend_stat(ZSTR_VAL(new_value), &buf) != 0 || !S_ISDIR(buf.st_mode) || #ifndef ZEND_WIN32 - access(ZSTR_VAL(new_value), R_OK | W_OK | X_OK) != 0) { + access(ZSTR_VAL(new_value), R_OK | X_OK) != 0) { #else - _access(ZSTR_VAL(new_value), 06) != 0) { + _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; @@ -311,9 +311,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_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 @@ -823,6 +824,7 @@ ZEND_FUNCTION(opcache_get_configuration) #endif add_assoc_string(&directives, "opcache.file_cache", ZCG(accel_directives).file_cache ? ZCG(accel_directives).file_cache : ""); + add_assoc_bool(&directives, "opcache.file_cache_read_only", ZCG(accel_directives).file_cache_read_only); add_assoc_bool(&directives, "opcache.file_cache_only", ZCG(accel_directives).file_cache_only); add_assoc_bool(&directives, "opcache.file_cache_consistency_checks", ZCG(accel_directives).file_cache_consistency_checks); #if ENABLE_FILE_CACHE_FALLBACK diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 736d644516a..cc22c78d825 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -1100,6 +1100,10 @@ int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm) } #endif + if (ZCG(accel_directives).file_cache_read_only) { + return FAILURE; + } + filename = zend_file_cache_get_bin_file_path(script->script.filename); if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {