#include "zend_accelerator_hash.h"
#include "ext/pcre/php_pcre.h"
+#ifdef HAVE_OPCACHE_FILE_CACHE
+# include "zend_file_cache.h"
+# include "ext/standard/md5.h"
+#endif
+
#ifndef ZEND_WIN32
#include <netdb.h>
#endif
uint idx;
Bucket *p;
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (ZCG(accel_directives).file_cache_only) {
+ return str;
+ }
+#endif
+
if (IS_ACCEL_INTERNED(str)) {
/* this is already an interned string */
return str;
}
#endif
-static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
+accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
{
zend_stat_t statbuf;
#ifdef ZEND_WIN32
}
}
-static unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script)
-{
- signed char *mem = (signed char*)persistent_script->mem;
- size_t size = persistent_script->size;
- size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script;
- unsigned int checksum = ADLER32_INIT;
-
- if (mem < (signed char*)persistent_script) {
- checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem);
- size -= (signed char*)persistent_script - mem;
- mem += (signed char*)persistent_script - mem;
- }
-
- zend_adler32(checksum, mem, persistent_script_check_block_size);
- mem += sizeof(*persistent_script);
- size -= sizeof(*persistent_script);
-
- if (size > 0) {
- checksum = zend_adler32(checksum, mem, size);
- }
- return checksum;
-}
-
/* Instead of resolving full real path name each time we need to identify file,
* we create a key that consist from requested file name, current working
* directory, current include_path, etc */
zend_persistent_script *persistent_script;
if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (ZCG(accel_directives).file_cache) {
+ realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
+ if (realpath) {
+ zend_file_cache_invalidate(realpath);
+ zend_string_release(realpath);
+ }
+ }
+#endif
return FAILURE;
}
return FAILURE;
}
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (ZCG(accel_directives).file_cache) {
+ zend_file_cache_invalidate(realpath);
+ }
+#endif
+
persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
if (persistent_script && !persistent_script->corrupted) {
zend_file_handle file_handle;
}
accelerator_shm_read_unlock();
- efree(realpath);
+ zend_string_release(realpath);
return SUCCESS;
}
}
}
+#ifdef HAVE_OPCACHE_FILE_CACHE
+static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *new_persistent_script, int *from_shared_memory)
+{
+ uint memory_used;
+
+ /* Check if script may be stored in shared memory */
+ if (!zend_accel_script_persistable(new_persistent_script)) {
+ return new_persistent_script;
+ }
+
+ if (!zend_accel_script_optimize(new_persistent_script)) {
+ return new_persistent_script;
+ }
+
+ zend_shared_alloc_init_xlat_table();
+
+ /* Calculate the required memory size */
+ memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0);
+
+ /* Allocate memory block */
+#ifdef __SSE2__
+ /* Align to 64-byte boundary */
+ ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 64);
+ ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
+#else
+ ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used);
+#endif
+
+ /* Copy into shared memory */
+ new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0);
+
+ zend_shared_alloc_destroy_xlat_table();
+
+ new_persistent_script->is_phar =
+ new_persistent_script->full_path &&
+ strstr(new_persistent_script->full_path->val, ".phar") &&
+ !strstr(new_persistent_script->full_path->val, "://");
+
+ /* Consistency check */
+ if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
+ zend_accel_error(
+ ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
+ "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
+ new_persistent_script->full_path->val,
+ new_persistent_script->mem,
+ (char *)new_persistent_script->mem + new_persistent_script->size,
+ ZCG(mem));
+ }
+
+ new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
+
+ zend_file_cache_script_store(new_persistent_script);
+
+ *from_shared_memory = 1;
+ return new_persistent_script;
+}
+#endif
+
static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory)
{
zend_accel_hash_entry *bucket;
}
}
+
+ zend_shared_alloc_init_xlat_table();
+
/* Calculate the required memory size */
memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length);
ZCG(mem) = zend_shared_alloc(memory_used);
#endif
if (!ZCG(mem)) {
+ zend_shared_alloc_destroy_xlat_table();
zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
zend_shared_alloc_unlock();
return new_persistent_script;
/* Copy into shared memory */
new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length);
+ zend_shared_alloc_destroy_xlat_table();
+
new_persistent_script->is_phar =
new_persistent_script->full_path &&
strstr(new_persistent_script->full_path->val, ".phar") &&
zend_shared_alloc_unlock();
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (ZCG(accel_directives).file_cache) {
+ SHM_PROTECT();
+ zend_file_cache_script_store(new_persistent_script);
+ SHM_UNPROTECT();
+ }
+#endif
+
*from_shared_memory = 1;
return new_persistent_script;
}
}
}
-static zend_persistent_script *compile_and_cache_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p, int *from_shared_memory)
+static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p)
{
zend_persistent_script *new_persistent_script;
zend_op_array *orig_active_op_array;
zend_string_hash_val(new_persistent_script->full_path);
/* Now persistent_script structure is ready in process memory */
- return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory);
+ return new_persistent_script;
}
+#ifdef HAVE_OPCACHE_FILE_CACHE
+zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
+{
+ zend_persistent_script *persistent_script;
+ zend_op_array *op_array = NULL;
+ int from_memory; /* if the script we've got is stored in SHM */
+
+ if (is_stream_path(file_handle->filename) &&
+ !is_cacheable_stream_path(file_handle->filename)) {
+ return accelerator_orig_compile_file(file_handle, type);
+ }
+
+ if (!file_handle->opened_path) {
+ if (file_handle->type == ZEND_HANDLE_FILENAME &&
+ accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
+ if (type == ZEND_REQUIRE) {
+ zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
+ zend_bailout();
+ } else {
+ zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
+ }
+ return NULL;
+ }
+ }
+
+ persistent_script = zend_file_cache_script_load(file_handle);
+ if (persistent_script) {
+ /* see bug #15471 (old BTS) */
+ if (persistent_script->full_path) {
+ if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
+ !EG(current_execute_data)->func ||
+ !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
+ EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
+ (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
+ EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
+ if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
+ /* ext/phar has to load phar's metadata into memory */
+ if (persistent_script->is_phar) {
+ php_stream_statbuf ssb;
+ char *fname = emalloc(sizeof("phar://") + persistent_script->full_path->len);
+
+ memcpy(fname, "phar://", sizeof("phar://") - 1);
+ memcpy(fname + sizeof("phar://") - 1, persistent_script->full_path->val, persistent_script->full_path->len + 1);
+ php_stream_stat_path(fname, &ssb);
+ efree(fname);
+ }
+ }
+ }
+ }
+ zend_file_handle_dtor(file_handle);
+
+ persistent_script->dynamic_members.last_used = ZCG(request_time);
+
+ if (persistent_script->ping_auto_globals_mask) {
+ zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
+ }
+
+ return zend_accel_load_script(persistent_script, 1);
+ }
+
+ persistent_script = opcache_compile_file(file_handle, type, NULL, 0, &op_array);
+
+ if (persistent_script) {
+ from_memory = 0;
+ persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
+ return zend_accel_load_script(persistent_script, from_memory);
+ }
+
+ return op_array;
+}
+#endif
+
/* zend_compile() replacement */
zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
{
int key_length;
int from_shared_memory; /* if the script we've got is stored in SHM */
- if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok ||
- (!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
- (ZCSG(restart_in_progress) && accel_restart_is_active())) {
+ if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok) {
/* The Accelerator is disabled, act as if without the Accelerator */
return accelerator_orig_compile_file(file_handle, type);
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ } else if (ZCG(accel_directives).file_cache_only) {
+ return file_cache_compile_file(file_handle, type);
+#endif
+ } else if ((!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
+ (ZCSG(restart_in_progress) && accel_restart_is_active())) {
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (ZCG(accel_directives).file_cache) {
+ return file_cache_compile_file(file_handle, type);
+ }
+#endif
+ return accelerator_orig_compile_file(file_handle, type);
}
/* In case this callback is called from include_once, require_once or it's
}
}
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ /* Check the second level cache */
+ if (!persistent_script && ZCG(accel_directives).file_cache) {
+ persistent_script = zend_file_cache_script_load(file_handle);
+ }
+#endif
+
/* If script was not found or invalidated by validate_timestamps */
if (!persistent_script) {
uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
* If it isn't compile_and_cache_file() changes the flag to 0
*/
from_shared_memory = 0;
- persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory);
+ persistent_script = opcache_compile_file(file_handle, type, key, key ? key_length : 0, &op_array);
+ if (persistent_script) {
+ persistent_script = cache_script_in_shared_memory(persistent_script, key, key ? key_length : 0, &from_shared_memory);
+ }
/* Caching is disabled, returning op_array;
* or something went wrong during compilation, returning NULL
zend_accel_copy_internal_functions();
}
- SHM_UNPROTECT();
/* PHP-5.4 and above return "double", but we use 1 sec precision */
ZCG(auto_globals_mask) = 0;
ZCG(request_time) = (time_t)sapi_get_request_time();
ZCG(include_path_key_len) = 0;
ZCG(include_path_check) = 1;
+ /* check if ZCG(function_table) wasn't somehow polluted on the way */
+ if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
+ zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
+ }
+
+ ZCG(cwd) = NULL;
+ ZCG(cwd_key_len) = 0;
+ ZCG(cwd_check) = 1;
+
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (ZCG(accel_directives).file_cache_only) {
+ return;
+ }
+#endif
+
+ SHM_UNPROTECT();
+
if (ZCG(counted)) {
#ifdef ZTS
zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
zend_shared_alloc_unlock();
}
- /* check if ZCG(function_table) wasn't somehow polluted on the way */
- if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
- zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
- }
-
- ZCG(cwd) = NULL;
- ZCG(cwd_key_len) = 0;
- ZCG(cwd_check) = 1;
-
SHM_PROTECT();
if (ZCSG(last_restart_time) != ZCG(last_restart_time)) {
}
}
+#ifdef HAVE_OPCACHE_FILE_CACHE
+
+#define ZEND_BIN_ID "BIN_" ZEND_TOSTR(SIZEOF_CHAR) ZEND_TOSTR(SIZEOF_INT) ZEND_TOSTR(SIZEOF_LONG) ZEND_TOSTR(SIZEOF_SIZE_T) ZEND_TOSTR(SIZEOF_ZEND_LONG) ZEND_TOSTR(ZEND_MM_ALIGNMENT)
+
+static void accel_gen_system_id(void)
+{
+ PHP_MD5_CTX context;
+ unsigned char digest[16], c;
+ char *md5str = ZCG(system_id);
+ int i;
+
+ PHP_MD5Init(&context);
+ PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
+ PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
+ PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
+ if (strstr(PHP_VERSION, "-dev") != 0) {
+ /* Development versions may be changed from build to build */
+ PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
+ PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
+ }
+ PHP_MD5Final(digest, &context);
+ for (i = 0; i < 16; i++) {
+ c = digest[i] >> 4;
+ c = (c <= 9) ? c + '0' : c - 10 + 'a';
+ md5str[i * 2] = c;
+ c = digest[i] & 0x0f;
+ c = (c <= 9) ? c + '0' : c - 10 + 'a';
+ md5str[(i * 2) + 1] = c;
+ }
+}
+#endif
+
static int accel_startup(zend_extension *extension)
{
zend_function *func;
return FAILURE;
}
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ accel_gen_system_id();
+#endif
+
/* no supported SAPI found - disable acceleration and stop initialization */
if (accel_find_sapi() == FAILURE) {
accel_startup_ok = 0;
if (ZCG(enabled) == 0) {
return SUCCESS ;
}
+
/********************************************/
/* End of non-SHM dependent initializations */
/********************************************/
- switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
- case ALLOC_SUCCESS:
- if (zend_accel_init_shm() == FAILURE) {
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ if (!ZCG(accel_directives).file_cache_only) {
+#else
+ if (1) {
+#endif
+ switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
+ case ALLOC_SUCCESS:
+ if (zend_accel_init_shm() == FAILURE) {
+ accel_startup_ok = 0;
+ return FAILURE;
+ }
+ break;
+ case ALLOC_FAILURE:
accel_startup_ok = 0;
- return FAILURE;
- }
- break;
- case ALLOC_FAILURE:
- accel_startup_ok = 0;
- zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
- return SUCCESS;
- case SUCCESSFULLY_REATTACHED:
- accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
- zend_shared_alloc_lock();
- orig_new_interned_string = zend_new_interned_string;
- orig_interned_strings_snapshot = zend_interned_strings_snapshot;
- orig_interned_strings_restore = zend_interned_strings_restore;
-
- zend_new_interned_string = accel_new_interned_string_for_php;
- zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
- zend_interned_strings_restore = accel_interned_strings_restore_for_php;
+ zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
+ return SUCCESS;
+ case SUCCESSFULLY_REATTACHED:
+ accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
+ zend_shared_alloc_lock();
+ orig_new_interned_string = zend_new_interned_string;
+ orig_interned_strings_snapshot = zend_interned_strings_snapshot;
+ orig_interned_strings_restore = zend_interned_strings_restore;
+
+ zend_new_interned_string = accel_new_interned_string_for_php;
+ zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
+ zend_interned_strings_restore = accel_interned_strings_restore_for_php;
#ifndef ZTS
- accel_use_shm_interned_strings();
+ accel_use_shm_interned_strings();
#endif
- zend_shared_alloc_unlock();
- break;
- case FAILED_REATTACHED:
- accel_startup_ok = 0;
- zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
- return SUCCESS;
- break;
- }
+ zend_shared_alloc_unlock();
+ break;
+ case FAILED_REATTACHED:
+ accel_startup_ok = 0;
+ zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
+ return SUCCESS;
+ break;
+ }
- /* remeber the last restart time in the process memory */
- ZCG(last_restart_time) = ZCSG(last_restart_time);
+ /* from this point further, shared memory is supposed to be OK */
- /* from this point further, shared memory is supposed to be OK */
+ /* remeber the last restart time in the process memory */
+ ZCG(last_restart_time) = ZCSG(last_restart_time);
+
+ /* Init auto-global strings */
+ zend_accel_init_auto_globals();
+
+ zend_shared_alloc_lock();
+ zend_shared_alloc_save_state();
+ zend_shared_alloc_unlock();
+
+ SHM_PROTECT();
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ } else if (!ZCG(accel_directives).file_cache) {
+ accel_startup_ok = 0;
+ zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
+ return SUCCESS;
+ } else {
+ accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
- /* Init auto-global strings */
- zend_accel_init_auto_globals();
+ /* Init auto-global strings */
+ zend_accel_init_auto_globals();
+#endif
+ }
/* Override compiler */
accelerator_orig_compile_file = zend_compile_file;
ini_entry->on_modify = accel_include_path_on_modify;
}
- zend_shared_alloc_lock();
- zend_shared_alloc_save_state();
- zend_shared_alloc_unlock();
-
- SHM_PROTECT();
-
accel_startup_ok = 1;
/* Override file_exists(), is_file() and is_readable() */
void accel_shutdown(void)
{
zend_ini_entry *ini_entry;
+ zend_bool file_cache_only = 0;
zend_accel_blacklist_shutdown(&accel_blacklist);
zend_interned_strings_snapshot = orig_interned_strings_snapshot;
zend_interned_strings_restore = orig_interned_strings_restore;
+#ifdef HAVE_OPCACHE_FILE_CACHE
+ file_cache_only = ZCG(accel_directives).file_cache_only;
+#endif
+
accel_free_ts_resources();
- zend_shared_alloc_shutdown();
+
+ if (!file_cache_only) {
+ zend_shared_alloc_shutdown();
+ }
zend_compile_file = accelerator_orig_compile_file;
if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2015 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "zend.h"
+#include "zend_virtual_cwd.h"
+#include "zend_compile.h"
+#include "zend_vm.h"
+
+#include "php.h"
+
+#ifdef HAVE_OPCACHE_FILE_CACHE
+
+#include "ZendAccelerator.h"
+#include "zend_file_cache.h"
+#include "zend_shared_alloc.h"
+#include "zend_accelerator_util_funcs.h"
+#include "zend_accelerator_hash.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_UIO_H
+# include <sys/uio.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
+#endif
+
+#ifdef ZEND_WIN32
+# define LOCK_SH 0
+# define LOCK_EX 1
+# define LOCK_UN 2
+static int zend_file_cache_flock(int fd, int op)
+{
+ OVERLAPPED offset = {0,0,0,0,NULL};
+ if (op == LOCK_EX) {
+ if (LockFileEx((HANDLE)_get_osfhandle(fd),
+ LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset) == TRUE) {
+ return 0;
+ }
+ } else if (op == LOCK_SH) {
+ if (LockFileEx((HANDLE)_get_osfhandle(fd),
+ 0, 0, 1, 0, &offset) == TRUE) {
+ return 0;
+ }
+ } else if (op == LOCK_UN) {
+ if (UnlockFileEx((HANDLE)_get_osfhandle(fd),
+ 0, 1, 0, &offset) == TRUE) {
+ return 0;
+ }
+ }
+ return -1;
+}
+#elif defined(HAVE_FLOCK)
+# define zend_file_cache_flock flock
+#else
+# define LOCK_SH 0
+# define LOCK_EX 1
+# define LOCK_UN 2
+static int zend_file_cache_flock(int fd, int type)
+{
+ return 0;
+}
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#define SUFFIX ".bin"
+
+#define IS_SERIALIZED_INTERNED(ptr) \
+ ((size_t)(ptr) & Z_UL(1))
+#define IS_SERIALIZED(ptr) \
+ ((char*)(ptr) < (char*)script->size)
+#define IS_UNSERIALIZED(ptr) \
+ (((char*)(ptr) >= (char*)script->mem && (char*)(ptr) < (char*)script->mem + script->size) || \
+ IS_ACCEL_INTERNED(ptr))
+#define SERIALIZE_PTR(ptr) do { \
+ if (ptr) { \
+ if (IS_ACCEL_INTERNED(ptr)) { \
+ (ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
+ } else { \
+ ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
+ (ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
+ } \
+ } \
+ } while (0)
+#define UNSERIALIZE_PTR(ptr) do { \
+ if (ptr) { \
+ if (IS_SERIALIZED_INTERNED(ptr)) { \
+ (ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr)); \
+ } else { \
+ ZEND_ASSERT(IS_SERIALIZED(ptr)); \
+ (ptr) = (void*)((char*)buf + (size_t)(ptr)); \
+ } \
+ } \
+ } while (0)
+
+static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
+ {HT_INVALID_IDX, HT_INVALID_IDX};
+
+typedef struct _zend_file_cache_metainfo {
+ char magic[8];
+ char system_id[32];
+ size_t mem_size;
+ size_t str_size;
+ size_t script_offset;
+ accel_time_t timestamp;
+ uint32_t checksum;
+} zend_file_cache_metainfo;
+
+static int zend_file_cache_mkdir(char *filename, size_t start)
+{
+ char *s = filename + start;
+
+ while (*s) {
+ if (IS_SLASH(*s)) {
+ char old = *s;
+ *s = '\000';
+ if (mkdir(filename, S_IRWXU) < 0 && errno != EEXIST) {
+ *s = old;
+ return FAILURE;
+ }
+ *s = old;
+ }
+ s++;
+ }
+ return SUCCESS;
+}
+
+typedef void (*serialize_callback_t)(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf);
+
+typedef void (*unserialize_callback_t)(zval *zv,
+ zend_persistent_script *script,
+ void *buf);
+
+static void zend_file_cache_serialize_zval(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf);
+static void zend_file_cache_unserialize_zval(zval *zv,
+ zend_persistent_script *script,
+ void *buf);
+
+static void *zend_file_cache_serialize_interned(zend_string *str,
+ zend_file_cache_metainfo *info)
+{
+ size_t len;
+ void *ret;
+
+ /* check if the same interned string was already stored */
+ ret = zend_shared_alloc_get_xlat_entry(str);
+ if (ret) {
+ return ret;
+ }
+
+ len = ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + str->len + 1);
+ ret = (void*)(info->str_size | Z_UL(1));
+ zend_shared_alloc_register_xlat_entry(str, ret);
+ if (info->str_size + len > ((zend_string*)ZCG(mem))->len) {
+ size_t new_len = info->str_size + len;
+ ZCG(mem) = (void*)zend_string_realloc(
+ (zend_string*)ZCG(mem),
+ ((_STR_HEADER_SIZE + 1 + new_len + 4095) & ~0xfff) - (_STR_HEADER_SIZE + 1),
+ 0);
+ }
+ memcpy(((zend_string*)ZCG(mem))->val + info->str_size, str, len);
+ info->str_size += len;
+ return ret;
+}
+
+static void *zend_file_cache_unserialize_interned(zend_string *str)
+{
+ zend_string *ret;
+
+ str = (zend_string*)((char*)ZCG(mem) + ((size_t)(str) & ~Z_UL(1)));
+ ret = accel_new_interned_string(str);
+ if (ret == str) {
+ /* String wasn't interned but we will use it as interned anyway */
+ GC_FLAGS(ret) |= IS_STR_INTERNED | IS_STR_PERMANENT;
+ }
+ return ret;
+}
+
+static void zend_file_cache_serialize_hash(HashTable *ht,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf,
+ serialize_callback_t func)
+{
+ Bucket *p, *end;
+
+ if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
+ ht->arData = NULL;
+ return;
+ }
+ if (IS_SERIALIZED(ht->arData)) {
+ return;
+ }
+ SERIALIZE_PTR(ht->arData);
+ p = ht->arData;
+ UNSERIALIZE_PTR(p);
+ end = p + ht->nNumUsed;
+ while (p < end) {
+ if (Z_TYPE(p->val) != IS_UNDEF) {
+ SERIALIZE_PTR(p->key);
+ func(&p->val, script, info, buf);
+ }
+ p++;
+ }
+}
+
+static zend_ast *zend_file_cache_serialize_ast(zend_ast *ast,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ uint32_t i;
+ zend_ast *ret;
+
+ SERIALIZE_PTR(ast);
+ ret = ast;
+ UNSERIALIZE_PTR(ast);
+
+ if (ast->kind == ZEND_AST_ZVAL) {
+ zend_file_cache_serialize_zval(&((zend_ast_zval*)ast)->val, script, info, buf);
+ } else if (zend_ast_is_list(ast)) {
+ zend_ast_list *list = zend_ast_get_list(ast);
+ for (i = 0; i < list->children; i++) {
+ if (list->child[i]) {
+ list->child[i] = zend_file_cache_serialize_ast(list->child[i], script, info, buf);
+ }
+ }
+ } else {
+ uint32_t children = zend_ast_get_num_children(ast);
+ for (i = 0; i < children; i++) {
+ if (ast->child[i]) {
+ ast->child[i] = zend_file_cache_serialize_ast(ast->child[i], script, info, buf);
+ }
+ }
+ }
+ return ret;
+}
+
+static void zend_file_cache_serialize_zval(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ switch (Z_TYPE_P(zv)) {
+ case IS_STRING:
+ case IS_CONSTANT:
+ if (!IS_SERIALIZED(Z_STR_P(zv))) {
+ SERIALIZE_PTR(Z_STR_P(zv));
+ }
+ break;
+ case IS_ARRAY:
+ if (!IS_SERIALIZED(Z_ARR_P(zv))) {
+ HashTable *ht;
+
+ SERIALIZE_PTR(Z_ARR_P(zv));
+ ht = Z_ARR_P(zv);
+ UNSERIALIZE_PTR(ht);
+ zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
+ }
+ break;
+ case IS_REFERENCE:
+ if (!IS_SERIALIZED(Z_REF_P(zv))) {
+ zend_reference *ref;
+
+ SERIALIZE_PTR(Z_REF_P(zv));
+ ref = Z_REF_P(zv);
+ UNSERIALIZE_PTR(ref);
+ zend_file_cache_serialize_zval(&ref->val, script, info, buf);
+ }
+ break;
+ case IS_CONSTANT_AST:
+ if (!IS_SERIALIZED(Z_AST_P(zv))) {
+ zend_ast_ref *ast;
+
+ SERIALIZE_PTR(Z_AST_P(zv));
+ ast = Z_AST_P(zv);
+ UNSERIALIZE_PTR(ast);
+ if (!IS_SERIALIZED(ast->ast)) {
+ ast->ast = zend_file_cache_serialize_ast(ast->ast, script, info, buf);
+ }
+ }
+ break;
+ }
+}
+
+static void zend_file_cache_serialize_op_array(zend_op_array *op_array,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ if (op_array->static_variables && !IS_SERIALIZED(op_array->static_variables)) {
+ HashTable *ht;
+
+ SERIALIZE_PTR(op_array->static_variables);
+ ht = op_array->static_variables;
+ UNSERIALIZE_PTR(ht);
+ zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
+ }
+
+ if (op_array->literals && !IS_SERIALIZED(op_array->literals)) {
+ zval *p, *end;
+
+ SERIALIZE_PTR(op_array->literals);
+ p = op_array->literals;
+ UNSERIALIZE_PTR(p);
+ end = p + op_array->last_literal;
+ while (p < end) {
+ zend_file_cache_serialize_zval(p, script, info, buf);
+ p++;
+ }
+ }
+
+ if (!IS_SERIALIZED(op_array->opcodes)) {
+#if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR
+ zend_op *opline, *end;
+
+ SERIALIZE_PTR(op_array->opcodes);
+ opline = op_array->opcodes;
+ UNSERIALIZE_PTR(opline);
+ end = opline + op_array->last;
+ while (opline < end) {
+# if ZEND_USE_ABS_CONST_ADDR
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ SERIALIZE_PTR(opline->op1.zv);
+ }
+ if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ SERIALIZE_PTR(opline->op2.zv);
+ }
+# endif
+# if ZEND_USE_ABS_JMP_ADDR
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_GOTO:
+ case ZEND_FAST_CALL:
+ SERIALIZE_PTR(opline->op1.jmp_addr);
+ break;
+ case ZEND_JMPZNZ:
+ /* relative extended_value don't have to be changed */
+ /* break omitted intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_NEW:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_ASSERT_CHECK:
+ SERIALIZE_PTR(opline->op2.jmp_addr);
+ break;
+ }
+# endif
+ opline++;
+ }
+#else
+ SERIALIZE_PTR(op_array->opcodes);
+#endif
+
+ if (op_array->arg_info) {
+ zend_arg_info *p, *end;
+ SERIALIZE_PTR(op_array->arg_info);
+ p = op_array->arg_info;
+ UNSERIALIZE_PTR(p);
+ end = p + op_array->num_args;
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ p--;
+ }
+ if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
+ end++;
+ }
+ while (p < end) {
+ if (!IS_SERIALIZED(p->name)) {
+ SERIALIZE_PTR(p->name);
+ }
+ if (!IS_SERIALIZED(p->class_name)) {
+ SERIALIZE_PTR(p->class_name);
+ }
+ p++;
+ }
+ }
+
+ if (op_array->vars) {
+ zend_string **p, **end;
+
+ SERIALIZE_PTR(op_array->vars);
+ p = op_array->vars;
+ UNSERIALIZE_PTR(p);
+ end = p + op_array->last_var;
+ while (p < end) {
+ if (!IS_SERIALIZED(*p)) {
+ SERIALIZE_PTR(*p);
+ }
+ p++;
+ }
+ }
+
+ SERIALIZE_PTR(op_array->function_name);
+ SERIALIZE_PTR(op_array->filename);
+ SERIALIZE_PTR(op_array->brk_cont_array);
+ SERIALIZE_PTR(op_array->scope);
+ SERIALIZE_PTR(op_array->doc_comment);
+ SERIALIZE_PTR(op_array->try_catch_array);
+ SERIALIZE_PTR(op_array->prototype);
+ }
+}
+
+static void zend_file_cache_serialize_func(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ zend_op_array *op_array;
+
+ SERIALIZE_PTR(Z_PTR_P(zv));
+ op_array = Z_PTR_P(zv);
+ UNSERIALIZE_PTR(op_array);
+ zend_file_cache_serialize_op_array(op_array, script, info, buf);
+}
+
+static void zend_file_cache_serialize_prop_info(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ if (!IS_SERIALIZED(Z_PTR_P(zv))) {
+ zend_property_info *prop;
+
+ SERIALIZE_PTR(Z_PTR_P(zv));
+ prop = Z_PTR_P(zv);
+ UNSERIALIZE_PTR(prop);
+
+ if (prop->ce && !IS_SERIALIZED(prop->ce)) {
+ SERIALIZE_PTR(prop->ce);
+ }
+ if (prop->name && !IS_SERIALIZED(prop->name)) {
+ SERIALIZE_PTR(prop->name);
+ }
+ if (prop->doc_comment && !IS_SERIALIZED(prop->doc_comment)) {
+ SERIALIZE_PTR(prop->doc_comment);
+ }
+ }
+}
+
+static void zend_file_cache_serialize_class(zval *zv,
+ zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ zend_class_entry *ce;
+
+ SERIALIZE_PTR(Z_PTR_P(zv));
+ ce = Z_PTR_P(zv);
+ UNSERIALIZE_PTR(ce);
+
+ SERIALIZE_PTR(ce->name);
+ zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func);
+ if (ce->default_properties_table) {
+ zval *p, *end;
+
+ SERIALIZE_PTR(ce->default_properties_table);
+ p = ce->default_properties_table;
+ UNSERIALIZE_PTR(p);
+ end = p + ce->default_properties_count;
+ while (p < end) {
+ zend_file_cache_serialize_zval(p, script, info, buf);
+ p++;
+ }
+ }
+ if (ce->default_static_members_table) {
+ zval *p, *end;
+
+ SERIALIZE_PTR(ce->default_static_members_table);
+ p = ce->default_static_members_table;
+ UNSERIALIZE_PTR(p);
+ end = p + ce->default_static_members_count;
+ while (p < end) {
+ zend_file_cache_serialize_zval(p, script, info, buf);
+ p++;
+ }
+ }
+ zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_zval);
+ SERIALIZE_PTR(ZEND_CE_FILENAME(ce));
+ SERIALIZE_PTR(ZEND_CE_DOC_COMMENT(ce));
+ zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
+
+ if (ce->trait_aliases) {
+ zend_trait_alias **p, *q;
+
+ SERIALIZE_PTR(ce->trait_aliases);
+ p = ce->trait_aliases;
+ UNSERIALIZE_PTR(p);
+
+ while (*p) {
+ SERIALIZE_PTR(*p);
+ q = *p;
+ UNSERIALIZE_PTR(q);
+
+ if (q->trait_method) {
+ zend_trait_method_reference *m;
+
+ SERIALIZE_PTR(q->trait_method);
+ m = q->trait_method;
+ UNSERIALIZE_PTR(m);
+
+ if (m->method_name) {
+ SERIALIZE_PTR(m->method_name);
+ }
+ if (m->class_name) {
+ SERIALIZE_PTR(m->class_name);
+ }
+ }
+
+ if (q->alias) {
+ SERIALIZE_PTR(q->alias);
+ }
+ p++;
+ }
+ }
+
+ if (ce->trait_precedences) {
+ zend_trait_precedence **p, *q;
+
+ SERIALIZE_PTR(ce->trait_precedences);
+ p = ce->trait_precedences;
+ UNSERIALIZE_PTR(p);
+
+ while (*p) {
+ SERIALIZE_PTR(*p);
+ q = *p;
+ UNSERIALIZE_PTR(q);
+
+ if (q->trait_method) {
+ zend_trait_method_reference *m;
+
+ SERIALIZE_PTR(q->trait_method);
+ m = q->trait_method;
+ UNSERIALIZE_PTR(m);
+
+ if (m->method_name) {
+ SERIALIZE_PTR(m->method_name);
+ }
+ if (m->class_name) {
+ SERIALIZE_PTR(m->class_name);
+ }
+ }
+
+ if (q->exclude_from_classes) {
+ zend_string **s;
+
+ SERIALIZE_PTR(q->exclude_from_classes);
+ s = (zend_string**)q->exclude_from_classes;
+ UNSERIALIZE_PTR(s);
+
+ while (*s) {
+ SERIALIZE_PTR(*s);
+ s++;
+ }
+ }
+ p++;
+ }
+ }
+
+ SERIALIZE_PTR(ce->parent);
+ SERIALIZE_PTR(ce->constructor);
+ SERIALIZE_PTR(ce->destructor);
+ SERIALIZE_PTR(ce->clone);
+ SERIALIZE_PTR(ce->__get);
+ SERIALIZE_PTR(ce->__set);
+ SERIALIZE_PTR(ce->__call);
+ SERIALIZE_PTR(ce->serialize_func);
+ SERIALIZE_PTR(ce->unserialize_func);
+ SERIALIZE_PTR(ce->__isset);
+ SERIALIZE_PTR(ce->__unset);
+ SERIALIZE_PTR(ce->__tostring);
+ SERIALIZE_PTR(ce->__callstatic);
+ SERIALIZE_PTR(ce->__debugInfo);
+}
+
+static void zend_file_cache_serialize(zend_persistent_script *script,
+ zend_file_cache_metainfo *info,
+ void *buf)
+{
+ zend_persistent_script *new_script;
+
+ memcpy(info->magic, "OPCACHE", 8);
+ memcpy(info->system_id, ZCG(system_id), 32);
+ info->mem_size = script->size;
+ info->str_size = 0;
+ info->script_offset = (char*)script - (char*)script->mem;
+ info->timestamp = script->timestamp;
+
+ memcpy(buf, script->mem, script->size);
+
+ new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
+ SERIALIZE_PTR(new_script->full_path);
+
+ zend_file_cache_serialize_hash(&new_script->class_table, script, info, buf, zend_file_cache_serialize_class);
+ zend_file_cache_serialize_hash(&new_script->function_table, script, info, buf, zend_file_cache_serialize_func);
+ zend_file_cache_serialize_op_array(&new_script->main_op_array, script, info, buf);
+
+ SERIALIZE_PTR(new_script->arena_mem);
+ new_script->mem = NULL;
+}
+
+int zend_file_cache_script_store(zend_persistent_script *script)
+{
+ size_t len;
+ int fd;
+ char *filename;
+ zend_file_cache_metainfo info;
+#ifndef ZEND_WIN32
+ struct iovec vec[3];
+#endif
+ void *mem, *buf;
+
+ len = strlen(ZCG(accel_directives).file_cache);
+ filename = emalloc(len + 33 + script->full_path->len + sizeof(SUFFIX));
+ memcpy(filename, ZCG(accel_directives).file_cache, len);
+ filename[len] = '/';
+ memcpy(filename + len + 1, ZCG(system_id), 32);
+ memcpy(filename + len + 33, script->full_path->val, script->full_path->len);
+ memcpy(filename + len + 33 + script->full_path->len, SUFFIX, sizeof(SUFFIX));
+
+ if (zend_file_cache_mkdir(filename, len) != SUCCESS) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s'\n", filename);
+ efree(filename);
+ return FAILURE;
+ }
+
+#ifndef ZEND_WIN32
+ fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+#else
+ fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, _S_IREAD | _S_IWRITE);
+#endif
+ if (fd < 0) {
+ if (errno != EEXIST) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s'\n", filename);
+ }
+ efree(filename);
+ return FAILURE;
+ }
+
+ if (zend_file_cache_flock(fd, LOCK_EX) != 0) {
+ close(fd);
+ efree(filename);
+ return FAILURE;
+ }
+
+#ifdef __SSE2__
+ /* Align to 64-byte boundary */
+ mem = emalloc(script->size + 64);
+ buf = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
+#else
+ mem = buf = emalloc(script->size);
+#endif
+
+ ZCG(mem) = zend_string_alloc(4096 - (_STR_HEADER_SIZE + 1), 0);
+
+ zend_shared_alloc_init_xlat_table();
+ zend_file_cache_serialize(script, &info, buf);
+ zend_shared_alloc_destroy_xlat_table();
+
+ info.checksum = zend_adler32(ADLER32_INIT, buf, script->size);
+ info.checksum = zend_adler32(info.checksum, (signed char*)((zend_string*)ZCG(mem))->val, info.str_size);
+
+#ifndef ZEND_WIN32
+ vec[0].iov_base = &info;
+ vec[0].iov_len = sizeof(info);
+ vec[1].iov_base = buf;
+ vec[1].iov_len = script->size;
+ vec[2].iov_base = ((zend_string*)ZCG(mem))->val;
+ vec[2].iov_len = info.str_size;
+
+ if (writev(fd, vec, 3) != (ssize_t)(sizeof(info) + script->size + info.str_size)) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
+ zend_string_release((zend_string*)ZCG(mem));
+ efree(mem);
+ unlink(filename);
+ efree(filename);
+ return FAILURE;
+ }
+#else
+ if (ZEND_LONG_MAX < (zend_long)(sizeof(info) + script->size + info.str_size) ||
+ write(fd, &info, sizeof(info)) != sizeof(info) ||
+ write(fd, buf, script->size) != script->size ||
+ write(fd, ((zend_string*)ZCG(mem))->val, info.str_size) != info.str_size
+ ) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
+ zend_string_release((zend_string*)ZCG(mem));
+ efree(mem);
+ unlink(filename);
+ efree(filename);
+ return FAILURE;
+ }
+#endif
+
+ zend_string_release((zend_string*)ZCG(mem));
+ efree(mem);
+ if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
+ }
+ close(fd);
+ efree(filename);
+
+ return SUCCESS;
+}
+
+static void zend_file_cache_unserialize_hash(HashTable *ht,
+ zend_persistent_script *script,
+ void *buf,
+ unserialize_callback_t func)
+{
+ Bucket *p, *end;
+
+ if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
+ HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
+ return;
+ }
+ if (IS_UNSERIALIZED(ht->arData)) {
+ return;
+ }
+ UNSERIALIZE_PTR(ht->arData);
+ p = ht->arData;
+ end = p + ht->nNumUsed;
+ while (p < end) {
+ if (Z_TYPE(p->val) != IS_UNDEF) {
+ UNSERIALIZE_PTR(p->key);
+ func(&p->val, script, buf);
+ }
+ p++;
+ }
+}
+
+static zend_ast *zend_file_cache_unserialize_ast(zend_ast *ast,
+ zend_persistent_script *script,
+ void *buf)
+{
+ uint32_t i;
+
+ UNSERIALIZE_PTR(ast);
+
+ if (ast->kind == ZEND_AST_ZVAL) {
+ zend_file_cache_unserialize_zval(&((zend_ast_zval*)ast)->val, script, buf);
+ } else if (zend_ast_is_list(ast)) {
+ zend_ast_list *list = zend_ast_get_list(ast);
+ for (i = 0; i < list->children; i++) {
+ if (list->child[i]) {
+ list->child[i] = zend_file_cache_unserialize_ast(list->child[i], script, buf);
+ }
+ }
+ } else {
+ uint32_t children = zend_ast_get_num_children(ast);
+ for (i = 0; i < children; i++) {
+ if (ast->child[i]) {
+ ast->child[i] = zend_file_cache_unserialize_ast(ast->child[i], script, buf);
+ }
+ }
+ }
+ return ast;
+}
+
+static void zend_file_cache_unserialize_zval(zval *zv,
+ zend_persistent_script *script,
+ void *buf)
+{
+ switch (Z_TYPE_P(zv)) {
+ case IS_STRING:
+ case IS_CONSTANT:
+ if (!IS_UNSERIALIZED(Z_STR_P(zv))) {
+ UNSERIALIZE_PTR(Z_STR_P(zv));
+ }
+ break;
+ case IS_ARRAY:
+ if (!IS_UNSERIALIZED(Z_ARR_P(zv))) {
+ HashTable *ht;
+
+ UNSERIALIZE_PTR(Z_ARR_P(zv));
+ ht = Z_ARR_P(zv);
+ zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_zval);
+ }
+ break;
+ case IS_REFERENCE:
+ if (!IS_UNSERIALIZED(Z_REF_P(zv))) {
+ zend_reference *ref;
+
+ UNSERIALIZE_PTR(Z_REF_P(zv));
+ ref = Z_REF_P(zv);
+ zend_file_cache_unserialize_zval(&ref->val, script, buf);
+ }
+ break;
+ case IS_CONSTANT_AST:
+ if (!IS_UNSERIALIZED(Z_AST_P(zv))) {
+ zend_ast_ref *ast;
+
+ UNSERIALIZE_PTR(Z_AST_P(zv));
+ ast = Z_AST_P(zv);
+ if (!IS_UNSERIALIZED(ast->ast)) {
+ ast->ast = zend_file_cache_unserialize_ast(ast->ast, script, buf);
+ }
+ }
+ break;
+ }
+}
+
+static void zend_file_cache_unserialize_op_array(zend_op_array *op_array,
+ zend_persistent_script *script,
+ void *buf)
+{
+ if (op_array->static_variables && !IS_UNSERIALIZED(op_array->static_variables)) {
+ HashTable *ht;
+
+ UNSERIALIZE_PTR(op_array->static_variables);
+ ht = op_array->static_variables;
+ zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_zval);
+ }
+
+ if (op_array->literals && !IS_UNSERIALIZED(op_array->literals)) {
+ zval *p, *end;
+
+ UNSERIALIZE_PTR(op_array->literals);
+ p = op_array->literals;
+ end = p + op_array->last_literal;
+ while (p < end) {
+ zend_file_cache_unserialize_zval(p, script, buf);
+ p++;
+ }
+ }
+
+ if (!IS_UNSERIALIZED(op_array->opcodes)) {
+ zend_op *opline, *end;
+
+ UNSERIALIZE_PTR(op_array->opcodes);
+ opline = op_array->opcodes;
+ end = opline + op_array->last;
+ while (opline < end) {
+# if ZEND_USE_ABS_CONST_ADDR
+ if (ZEND_OP1_TYPE(opline) == IS_CONST) {
+ UNSERIALIZE_PTR(opline->op1.zv);
+ }
+ if (ZEND_OP2_TYPE(opline) == IS_CONST) {
+ UNSERIALIZE_PTR(opline->op2.zv);
+ }
+# endif
+# if ZEND_USE_ABS_JMP_ADDR
+ switch (opline->opcode) {
+ case ZEND_JMP:
+ case ZEND_GOTO:
+ case ZEND_FAST_CALL:
+ UNSERIALIZE_PTR(opline->op1.jmp_addr);
+ break;
+ case ZEND_JMPZNZ:
+ /* relative extended_value don't have to be changed */
+ /* break omitted intentionally */
+ case ZEND_JMPZ:
+ case ZEND_JMPNZ:
+ case ZEND_JMPZ_EX:
+ case ZEND_JMPNZ_EX:
+ case ZEND_JMP_SET:
+ case ZEND_COALESCE:
+ case ZEND_NEW:
+ case ZEND_FE_RESET_R:
+ case ZEND_FE_RESET_RW:
+ case ZEND_FE_FETCH_R:
+ case ZEND_FE_FETCH_RW:
+ case ZEND_ASSERT_CHECK:
+ UNSERIALIZE_PTR(opline->op2.jmp_addr);
+ break;
+ }
+# endif
+ ZEND_VM_SET_OPCODE_HANDLER(opline);
+ opline++;
+ }
+
+ if (op_array->arg_info) {
+ zend_arg_info *p, *end;
+ UNSERIALIZE_PTR(op_array->arg_info);
+ p = op_array->arg_info;
+ end = p + op_array->num_args;
+ if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ p--;
+ }
+ if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
+ end++;
+ }
+ while (p < end) {
+ if (!IS_UNSERIALIZED(p->name)) {
+ UNSERIALIZE_PTR(p->name);
+ }
+ if (!IS_UNSERIALIZED(p->class_name)) {
+ UNSERIALIZE_PTR(p->class_name);
+ }
+ p++;
+ }
+ }
+
+ if (op_array->vars) {
+ zend_string **p, **end;
+
+ UNSERIALIZE_PTR(op_array->vars);
+ p = op_array->vars;
+ end = p + op_array->last_var;
+ while (p < end) {
+ if (!IS_UNSERIALIZED(*p)) {
+ UNSERIALIZE_PTR(*p);
+ }
+ p++;
+ }
+ }
+
+ UNSERIALIZE_PTR(op_array->function_name);
+ UNSERIALIZE_PTR(op_array->filename);
+ UNSERIALIZE_PTR(op_array->brk_cont_array);
+ UNSERIALIZE_PTR(op_array->scope);
+ UNSERIALIZE_PTR(op_array->doc_comment);
+ UNSERIALIZE_PTR(op_array->try_catch_array);
+ UNSERIALIZE_PTR(op_array->prototype);
+ }
+}
+
+static void zend_file_cache_unserialize_func(zval *zv,
+ zend_persistent_script *script,
+ void *buf)
+{
+ zend_op_array *op_array;
+
+ UNSERIALIZE_PTR(Z_PTR_P(zv));
+ op_array = Z_PTR_P(zv);
+ zend_file_cache_unserialize_op_array(op_array, script, buf);
+}
+
+static void zend_file_cache_unserialize_prop_info(zval *zv,
+ zend_persistent_script *script,
+ void *buf)
+{
+ if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
+ zend_property_info *prop;
+
+ UNSERIALIZE_PTR(Z_PTR_P(zv));
+ prop = Z_PTR_P(zv);
+
+ if (prop->ce && !IS_UNSERIALIZED(prop->ce)) {
+ UNSERIALIZE_PTR(prop->ce);
+ }
+ if (prop->name && !IS_UNSERIALIZED(prop->name)) {
+ UNSERIALIZE_PTR(prop->name);
+ }
+ if (prop->doc_comment && !IS_UNSERIALIZED(prop->doc_comment)) {
+ UNSERIALIZE_PTR(prop->doc_comment);
+ }
+ }
+}
+
+static void zend_file_cache_unserialize_class(zval *zv,
+ zend_persistent_script *script,
+ void *buf)
+{
+ zend_class_entry *ce;
+
+ UNSERIALIZE_PTR(Z_PTR_P(zv));
+ ce = Z_PTR_P(zv);
+
+ UNSERIALIZE_PTR(ce->name);
+ zend_file_cache_unserialize_hash(&ce->function_table, script, buf, zend_file_cache_unserialize_func);
+ if (ce->default_properties_table) {
+ zval *p, *end;
+
+ UNSERIALIZE_PTR(ce->default_properties_table);
+ p = ce->default_properties_table;
+ end = p + ce->default_properties_count;
+ while (p < end) {
+ zend_file_cache_unserialize_zval(p, script, buf);
+ p++;
+ }
+ }
+ if (ce->default_static_members_table) {
+ zval *p, *end;
+
+ UNSERIALIZE_PTR(ce->default_static_members_table);
+ p = ce->default_static_members_table;
+ end = p + ce->default_static_members_count;
+ while (p < end) {
+ zend_file_cache_unserialize_zval(p, script, buf);
+ p++;
+ }
+ }
+ zend_file_cache_unserialize_hash(&ce->constants_table, script, buf, zend_file_cache_unserialize_zval);
+ UNSERIALIZE_PTR(ZEND_CE_FILENAME(ce));
+ UNSERIALIZE_PTR(ZEND_CE_DOC_COMMENT(ce));
+ zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info);
+
+ if (ce->trait_aliases) {
+ zend_trait_alias **p, *q;
+
+ UNSERIALIZE_PTR(ce->trait_aliases);
+ p = ce->trait_aliases;
+
+ while (*p) {
+ UNSERIALIZE_PTR(*p);
+ q = *p;
+
+ if (q->trait_method) {
+ zend_trait_method_reference *m;
+
+ UNSERIALIZE_PTR(q->trait_method);
+ m = q->trait_method;
+
+ if (m->method_name) {
+ UNSERIALIZE_PTR(m->method_name);
+ }
+ if (m->class_name) {
+ UNSERIALIZE_PTR(m->class_name);
+ }
+ }
+
+ if (q->alias) {
+ UNSERIALIZE_PTR(q->alias);
+ }
+ p++;
+ }
+ }
+
+ if (ce->trait_precedences) {
+ zend_trait_precedence **p, *q;
+
+ UNSERIALIZE_PTR(ce->trait_precedences);
+ p = ce->trait_precedences;
+
+ while (*p) {
+ UNSERIALIZE_PTR(*p);
+ q = *p;
+
+ if (q->trait_method) {
+ zend_trait_method_reference *m;
+
+ UNSERIALIZE_PTR(q->trait_method);
+ m = q->trait_method;
+
+ if (m->method_name) {
+ UNSERIALIZE_PTR(m->method_name);
+ }
+ if (m->class_name) {
+ UNSERIALIZE_PTR(m->class_name);
+ }
+ }
+
+ if (q->exclude_from_classes) {
+ zend_string **s;
+
+ UNSERIALIZE_PTR(q->exclude_from_classes);
+ s = (zend_string**)q->exclude_from_classes;
+
+ while (*s) {
+ UNSERIALIZE_PTR(*s);
+ s++;
+ }
+ }
+ p++;
+ }
+ }
+
+ UNSERIALIZE_PTR(ce->parent);
+ UNSERIALIZE_PTR(ce->constructor);
+ UNSERIALIZE_PTR(ce->destructor);
+ UNSERIALIZE_PTR(ce->clone);
+ UNSERIALIZE_PTR(ce->__get);
+ UNSERIALIZE_PTR(ce->__set);
+ UNSERIALIZE_PTR(ce->__call);
+ UNSERIALIZE_PTR(ce->serialize_func);
+ UNSERIALIZE_PTR(ce->unserialize_func);
+ UNSERIALIZE_PTR(ce->__isset);
+ UNSERIALIZE_PTR(ce->__unset);
+ UNSERIALIZE_PTR(ce->__tostring);
+ UNSERIALIZE_PTR(ce->__callstatic);
+ UNSERIALIZE_PTR(ce->__debugInfo);
+}
+
+static void zend_file_cache_unserialize(zend_persistent_script *script,
+ void *buf)
+{
+ script->mem = buf;
+
+ UNSERIALIZE_PTR(script->full_path);
+
+ zend_file_cache_unserialize_hash(&script->class_table, script, buf, zend_file_cache_unserialize_class);
+ zend_file_cache_unserialize_hash(&script->function_table, script, buf, zend_file_cache_unserialize_func);
+ zend_file_cache_unserialize_op_array(&script->main_op_array, script, buf);
+
+ UNSERIALIZE_PTR(script->arena_mem);
+}
+
+zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
+{
+ zend_string *full_path = file_handle->opened_path;
+ size_t len;
+ int fd;
+ char *filename;
+ zend_persistent_script *script;
+ zend_file_cache_metainfo info;
+ zend_accel_hash_entry *bucket;
+ void *mem, *checkpoint, *buf;
+ int cache_it = 1;
+
+ if (!full_path) {
+ return NULL;
+ }
+ len = strlen(ZCG(accel_directives).file_cache);
+ filename = emalloc(len + 33 + full_path->len + sizeof(SUFFIX));
+ memcpy(filename, ZCG(accel_directives).file_cache, len);
+ filename[len] = '/';
+ memcpy(filename + len + 1, ZCG(system_id), 32);
+ memcpy(filename + len + 33, full_path->val, full_path->len);
+ memcpy(filename + len + 33 + full_path->len, SUFFIX, sizeof(SUFFIX));
+
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ efree(filename);
+ return NULL;
+ }
+
+ if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
+ close(fd);
+ efree(filename);
+ return NULL;
+ }
+
+ if (read(fd, &info, sizeof(info)) != sizeof(info)) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
+ zend_file_cache_flock(fd, LOCK_UN);
+ close(fd);
+ unlink(filename);
+ efree(filename);
+ return NULL;
+ }
+
+ /* verify header */
+ if (memcmp(info.magic, "OPCACHE", 8) != 0 ||
+ memcmp(info.system_id, ZCG(system_id), 32) != 0) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
+ zend_file_cache_flock(fd, LOCK_UN);
+ close(fd);
+ unlink(filename);
+ efree(filename);
+ return NULL;
+ }
+
+ /* verify timestamp */
+ if (ZCG(accel_directives).validate_timestamps &&
+ zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
+ if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
+ }
+ close(fd);
+ unlink(filename);
+ efree(filename);
+ return NULL;
+ }
+
+ checkpoint = zend_arena_checkpoint(CG(arena));
+#ifdef __SSE2__
+ /* Align to 64-byte boundary */
+ mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
+ mem = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
+#else
+ mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
+#endif
+
+ if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
+ zend_file_cache_flock(fd, LOCK_UN);
+ close(fd);
+ unlink(filename);
+ zend_arena_release(&CG(arena), checkpoint);
+ efree(filename);
+ return NULL;
+ }
+ if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
+ zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
+ }
+ close(fd);
+
+ /* verify checksum */
+ if (ZCG(accel_directives).file_cache_consistency_checks &&
+ zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size) != info.checksum) {
+ zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s'\n", filename);
+ unlink(filename);
+ zend_arena_release(&CG(arena), checkpoint);
+ efree(filename);
+ return NULL;
+ }
+
+ if (!ZCG(accel_directives).file_cache_only) {
+ /* exclusive lock */
+ zend_shared_alloc_lock();
+
+ /* Check if we still need to put the file into the cache (may be it was
+ * already stored by another process. This final check is done under
+ * exclusive lock) */
+ bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
+ if (bucket) {
+ script = (zend_persistent_script *)bucket->data;
+ if (!script->corrupted) {
+ zend_shared_alloc_unlock();
+ zend_arena_release(&CG(arena), checkpoint);
+ efree(filename);
+ return script;
+ }
+ }
+
+ if (zend_accel_hash_is_full(&ZCSG(hash))) {
+ zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
+ ZSMMG(memory_exhausted) = 1;
+ zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
+ zend_shared_alloc_unlock();
+ goto use_process_mem;
+ }
+
+#ifdef __SSE2__
+ /* Align to 64-byte boundary */
+ buf = zend_shared_alloc(info.mem_size + 64);
+ buf = (void*)(((zend_uintptr_t)buf + 63L) & ~63L);
+#else
+ buf = zend_shared_alloc(info.mem_size);
+#endif
+
+ if (!buf) {
+ zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
+ zend_shared_alloc_unlock();
+ goto use_process_mem;
+ }
+ memcpy(buf, mem, info.mem_size);
+ } else {
+use_process_mem:
+ buf = mem;
+ cache_it = 0;
+ }
+
+ ZCG(mem) = ((char*)mem + info.mem_size);
+ script = (zend_persistent_script*)((char*)buf + info.script_offset);
+ zend_file_cache_unserialize(script, buf);
+
+ if (cache_it) {
+ script->dynamic_members.checksum = zend_accel_script_checksum(script);
+
+ zend_accel_hash_update(&ZCSG(hash), script->full_path->val, script->full_path->len, 0, script);
+
+ zend_shared_alloc_unlock();
+ zend_arena_release(&CG(arena), checkpoint);
+ }
+ efree(filename);
+
+ return script;
+}
+
+void zend_file_cache_invalidate(zend_string *full_path)
+{
+ size_t len;
+ char *filename;
+
+ len = strlen(ZCG(accel_directives).file_cache);
+ filename = emalloc(len + 33 + full_path->len + sizeof(SUFFIX));
+ memcpy(filename, ZCG(accel_directives).file_cache, len);
+ filename[len] = '/';
+ memcpy(filename + len + 1, ZCG(system_id), 32);
+ memcpy(filename + len + 33, full_path->val, full_path->len);
+ memcpy(filename + len + 33 + full_path->len, SUFFIX, sizeof(SUFFIX));
+
+ unlink(filename);
+ efree(filename);
+}
+
+#endif /* HAVE_OPCACHE_FILE_CACHE */