1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@php.net>                                 |
16    |          Zeev Suraski <zeev@php.net>                                 |
17    |          Stanislav Malyshev <stas@zend.com>                          |
18    |          Dmitry Stogov <dmitry@php.net>                              |
19    +----------------------------------------------------------------------+
20 */
21 
22 #include <time.h>
23 
24 #include "php.h"
25 #include "ZendAccelerator.h"
26 #include "zend_API.h"
27 #include "zend_shared_alloc.h"
28 #include "zend_accelerator_blacklist.h"
29 #include "php_ini.h"
30 #include "SAPI.h"
31 #include "zend_virtual_cwd.h"
32 #include "ext/standard/info.h"
33 #include "ext/standard/php_filestat.h"
34 
35 #define STRING_NOT_NULL(s) (NULL == (s)?"":s)
36 #define MIN_ACCEL_FILES 200
37 #define MAX_ACCEL_FILES 1000000
38 #define TOKENTOSTR(X) #X
39 
40 static zif_handler orig_file_exists = NULL;
41 static zif_handler orig_is_file = NULL;
42 static zif_handler orig_is_readable = NULL;
43 
44 ZEND_BEGIN_ARG_INFO(arginfo_opcache_none, 0)
45 ZEND_END_ARG_INFO()
46 
47 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_get_status, 0, 0, 0)
48 	ZEND_ARG_INFO(0, fetch_scripts)
49 ZEND_END_ARG_INFO()
50 
51 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_compile_file, 0, 0, 1)
52 	ZEND_ARG_INFO(0, file)
53 ZEND_END_ARG_INFO()
54 
55 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_invalidate, 0, 0, 1)
56 	ZEND_ARG_INFO(0, script)
57 	ZEND_ARG_INFO(0, force)
58 ZEND_END_ARG_INFO()
59 
60 ZEND_BEGIN_ARG_INFO_EX(arginfo_opcache_is_script_cached, 0, 0, 1)
61 	ZEND_ARG_INFO(0, script)
62 ZEND_END_ARG_INFO()
63 
64 /* User functions */
65 static ZEND_FUNCTION(opcache_reset);
66 static ZEND_FUNCTION(opcache_invalidate);
67 static ZEND_FUNCTION(opcache_is_script_cached);
68 
69 /* Private functions */
70 static ZEND_FUNCTION(opcache_get_status);
71 static ZEND_FUNCTION(opcache_compile_file);
72 static ZEND_FUNCTION(opcache_get_configuration);
73 
74 static const zend_function_entry accel_functions[] = {
75 	/* User functions */
76 	ZEND_FE(opcache_reset,					arginfo_opcache_none)
77 	ZEND_FE(opcache_invalidate,				arginfo_opcache_invalidate)
78 	ZEND_FE(opcache_compile_file,			arginfo_opcache_compile_file)
79 	ZEND_FE(opcache_is_script_cached,		arginfo_opcache_is_script_cached)
80 	/* Private functions */
81 	ZEND_FE(opcache_get_configuration,		arginfo_opcache_none)
82 	ZEND_FE(opcache_get_status,				arginfo_opcache_get_status)
83 	ZEND_FE_END
84 };
85 
validate_api_restriction(void)86 static int validate_api_restriction(void)
87 {
88 	if (ZCG(accel_directives).restrict_api && *ZCG(accel_directives).restrict_api) {
89 		size_t len = strlen(ZCG(accel_directives).restrict_api);
90 
91 		if (!SG(request_info).path_translated ||
92 		    strlen(SG(request_info).path_translated) < len ||
93 		    memcmp(SG(request_info).path_translated, ZCG(accel_directives).restrict_api, len) != 0) {
94 			zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " API is restricted by \"restrict_api\" configuration directive");
95 			return 0;
96 		}
97 	}
98 	return 1;
99 }
100 
ZEND_INI_MH(OnUpdateMemoryConsumption)101 static ZEND_INI_MH(OnUpdateMemoryConsumption)
102 {
103 	zend_long *p;
104 	zend_long memsize;
105 #ifndef ZTS
106 	char *base = (char *) mh_arg2;
107 #else
108 	char *base = (char *) ts_resource(*((int *) mh_arg2));
109 #endif
110 
111 	/* keep the compiler happy */
112 	(void)entry; (void)mh_arg2; (void)mh_arg3; (void)stage;
113 
114 	p = (zend_long *) (base + (size_t)mh_arg1);
115 	memsize = atoi(ZSTR_VAL(new_value));
116 	/* sanity check we must use at least 8 MB */
117 	if (memsize < 8) {
118 		const char *new_new_value = "8";
119 		zend_ini_entry *ini_entry;
120 
121 		memsize = 8;
122 		zend_accel_error(ACCEL_LOG_WARNING, "opcache.memory_consumption is set below the required 8MB.\n");
123 		zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB configuration.\n");
124 
125 		if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
126 					"opcache.memory_consumption",
127 					sizeof("opcache.memory_consumption")-1)) == NULL) {
128 			return FAILURE;
129 		}
130 
131 		ini_entry->value = zend_string_init_interned(new_new_value, 1, 1);
132 	}
133 	if (UNEXPECTED(memsize > ZEND_ULONG_MAX / (1024 * 1024))) {
134 		*p = ZEND_ULONG_MAX;
135 	} else {
136 		*p = memsize * (1024 * 1024);
137 	}
138 	return SUCCESS;
139 }
140 
ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)141 static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)
142 {
143 	zend_long *p;
144 	zend_long size;
145 #ifndef ZTS
146 	char *base = (char *) mh_arg2;
147 #else
148 	char *base = (char *) ts_resource(*((int *) mh_arg2));
149 #endif
150 
151 	/* keep the compiler happy */
152 	(void)entry; (void)mh_arg2; (void)mh_arg3; (void)stage;
153 
154 	p = (zend_long *) (base + (size_t)mh_arg1);
155 	size = atoi(ZSTR_VAL(new_value));
156 	/* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */
157 
158 	if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) {
159 		const char *new_new_value;
160 		zend_ini_entry *ini_entry;
161 
162 		if (size < MIN_ACCEL_FILES) {
163 			size = MIN_ACCEL_FILES;
164 			new_new_value = TOKENTOSTR(MIN_ACCEL_FILES);
165 			zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES);
166 			zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal configuration.\n");
167 		}
168 		if (size > MAX_ACCEL_FILES) {
169 			size = MAX_ACCEL_FILES;
170 			new_new_value = TOKENTOSTR(MAX_ACCEL_FILES);
171 			zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES);
172 			zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal configuration.\n");
173 		}
174 		if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
175 					"opcache.max_accelerated_files",
176 					sizeof("opcache.max_accelerated_files")-1)) == NULL) {
177 			return FAILURE;
178 		}
179 		ini_entry->value = zend_string_init_interned(new_new_value, strlen(new_new_value), 1);
180 	}
181 	*p = size;
182 	return SUCCESS;
183 }
184 
ZEND_INI_MH(OnUpdateMaxWastedPercentage)185 static ZEND_INI_MH(OnUpdateMaxWastedPercentage)
186 {
187 	double *p;
188 	zend_long percentage;
189 #ifndef ZTS
190 	char *base = (char *) mh_arg2;
191 #else
192 	char *base = (char *) ts_resource(*((int *) mh_arg2));
193 #endif
194 
195 	/* keep the compiler happy */
196 	(void)entry; (void)mh_arg2; (void)mh_arg3; (void)stage;
197 
198 	p = (double *) (base + (size_t)mh_arg1);
199 	percentage = atoi(ZSTR_VAL(new_value));
200 
201 	if (percentage <= 0 || percentage > 50) {
202 		const char *new_new_value = "5";
203 		zend_ini_entry *ini_entry;
204 
205 		percentage = 5;
206 		zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_wasted_percentage must be set between 1 and 50.\n");
207 		zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use 5%%.\n");
208 		if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
209 					"opcache.max_wasted_percentage",
210 					sizeof("opcache.max_wasted_percentage")-1)) == NULL) {
211 			return FAILURE;
212 		}
213 		ini_entry->value = zend_string_init_interned(new_new_value, strlen(new_new_value), 1);
214 	}
215 	*p = (double)percentage / 100.0;
216 	return SUCCESS;
217 }
218 
ZEND_INI_MH(OnEnable)219 static ZEND_INI_MH(OnEnable)
220 {
221 	if (stage == ZEND_INI_STAGE_STARTUP ||
222 	    stage == ZEND_INI_STAGE_SHUTDOWN ||
223 	    stage == ZEND_INI_STAGE_DEACTIVATE) {
224 		return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
225 	} else {
226 		/* It may be only temporary disabled */
227 		zend_bool *p;
228 #ifndef ZTS
229 		char *base = (char *) mh_arg2;
230 #else
231 		char *base = (char *) ts_resource(*((int *) mh_arg2));
232 #endif
233 
234 		p = (zend_bool *) (base+(size_t) mh_arg1);
235 		if ((ZSTR_LEN(new_value) == 2 && strcasecmp("on", ZSTR_VAL(new_value)) == 0) ||
236 		    (ZSTR_LEN(new_value) == 3 && strcasecmp("yes", ZSTR_VAL(new_value)) == 0) ||
237 		    (ZSTR_LEN(new_value) == 4 && strcasecmp("true", ZSTR_VAL(new_value)) == 0) ||
238 			atoi(ZSTR_VAL(new_value)) != 0) {
239 			zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " can't be temporary enabled (it may be only disabled till the end of request)");
240 			return FAILURE;
241 		} else {
242 			*p = 0;
243 			ZCG(accelerator_enabled) = 0;
244 			return SUCCESS;
245 		}
246 	}
247 }
248 
249 #ifdef HAVE_OPCACHE_FILE_CACHE
ZEND_INI_MH(OnUpdateFileCache)250 static ZEND_INI_MH(OnUpdateFileCache)
251 {
252 	if (new_value) {
253 		if (!ZSTR_LEN(new_value)) {
254 			new_value = NULL;
255 		} else {
256 			zend_stat_t buf;
257 
258 		    if (!IS_ABSOLUTE_PATH(ZSTR_VAL(new_value), ZSTR_LEN(new_value)) ||
259 			    zend_stat(ZSTR_VAL(new_value), &buf) != 0 ||
260 			    !S_ISDIR(buf.st_mode) ||
261 #ifndef ZEND_WIN32
262 				access(ZSTR_VAL(new_value), R_OK | W_OK | X_OK) != 0) {
263 #else
264 				_access(ZSTR_VAL(new_value), 06) != 0) {
265 #endif
266 				zend_accel_error(ACCEL_LOG_WARNING, "opcache.file_cache must be a full path of accessible directory.\n");
267 				new_value = NULL;
268 			}
269 		}
270 	}
271 	OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
272 	return SUCCESS;
273 }
274 #endif
275 
276 ZEND_INI_BEGIN()
277 	STD_PHP_INI_BOOLEAN("opcache.enable"             , "1", PHP_INI_ALL,    OnEnable,     enabled                             , zend_accel_globals, accel_globals)
278 	STD_PHP_INI_BOOLEAN("opcache.use_cwd"            , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd            , zend_accel_globals, accel_globals)
279 	STD_PHP_INI_BOOLEAN("opcache.validate_timestamps", "1", PHP_INI_ALL   , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals)
280 	STD_PHP_INI_BOOLEAN("opcache.validate_permission", "0", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.validate_permission, zend_accel_globals, accel_globals)
281 #ifndef ZEND_WIN32
282 	STD_PHP_INI_BOOLEAN("opcache.validate_root"      , "0", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.validate_root      , zend_accel_globals, accel_globals)
283 #endif
284 	STD_PHP_INI_BOOLEAN("opcache.dups_fix"           , "0", PHP_INI_ALL   , OnUpdateBool, accel_directives.ignore_dups        , zend_accel_globals, accel_globals)
285 	STD_PHP_INI_BOOLEAN("opcache.revalidate_path"    , "0", PHP_INI_ALL   , OnUpdateBool, accel_directives.revalidate_path    , zend_accel_globals, accel_globals)
286 
287 	STD_PHP_INI_ENTRY("opcache.log_verbosity_level"   , "1"   , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level,       zend_accel_globals, accel_globals)
288 	STD_PHP_INI_ENTRY("opcache.memory_consumption"    , "128"  , PHP_INI_SYSTEM, OnUpdateMemoryConsumption,    accel_directives.memory_consumption,        zend_accel_globals, accel_globals)
289 	STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "8"  , PHP_INI_SYSTEM, OnUpdateLong,                 accel_directives.interned_strings_buffer,   zend_accel_globals, accel_globals)
290 	STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "10000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles,	 accel_directives.max_accelerated_files,     zend_accel_globals, accel_globals)
291 	STD_PHP_INI_ENTRY("opcache.max_wasted_percentage" , "5"   , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage,	 accel_directives.max_wasted_percentage,     zend_accel_globals, accel_globals)
292 	STD_PHP_INI_ENTRY("opcache.consistency_checks"    , "0"   , PHP_INI_ALL   , OnUpdateLong,	             accel_directives.consistency_checks,        zend_accel_globals, accel_globals)
293 	STD_PHP_INI_ENTRY("opcache.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong,	             accel_directives.force_restart_timeout,     zend_accel_globals, accel_globals)
294 	STD_PHP_INI_ENTRY("opcache.revalidate_freq"       , "2"   , PHP_INI_ALL   , OnUpdateLong,	             accel_directives.revalidate_freq,           zend_accel_globals, accel_globals)
295 	STD_PHP_INI_ENTRY("opcache.file_update_protection", "2"   , PHP_INI_ALL   , OnUpdateLong,                accel_directives.file_update_protection,    zend_accel_globals, accel_globals)
296 	STD_PHP_INI_ENTRY("opcache.preferred_memory_model", ""    , PHP_INI_SYSTEM, OnUpdateStringUnempty,       accel_directives.memory_model,              zend_accel_globals, accel_globals)
297 	STD_PHP_INI_ENTRY("opcache.blacklist_filename"    , ""    , PHP_INI_SYSTEM, OnUpdateString,	             accel_directives.user_blacklist_filename,   zend_accel_globals, accel_globals)
298 	STD_PHP_INI_ENTRY("opcache.max_file_size"         , "0"   , PHP_INI_SYSTEM, OnUpdateLong,	             accel_directives.max_file_size,             zend_accel_globals, accel_globals)
299 
300 	STD_PHP_INI_BOOLEAN("opcache.protect_memory"        , "0"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.protect_memory,            zend_accel_globals, accel_globals)
301 	STD_PHP_INI_BOOLEAN("opcache.save_comments"         , "1"  , PHP_INI_SYSTEM, OnUpdateBool,                  accel_directives.save_comments,             zend_accel_globals, accel_globals)
302 
303 	STD_PHP_INI_ENTRY("opcache.optimization_level"    , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level,   zend_accel_globals, accel_globals)
304 	STD_PHP_INI_ENTRY("opcache.opt_debug_level"       , "0"      , PHP_INI_SYSTEM, OnUpdateLong,             accel_directives.opt_debug_level,            zend_accel_globals, accel_globals)
305 	STD_PHP_INI_BOOLEAN("opcache.enable_file_override"	, "0"   , PHP_INI_SYSTEM, OnUpdateBool,              accel_directives.file_override_enabled,     zend_accel_globals, accel_globals)
306 	STD_PHP_INI_BOOLEAN("opcache.enable_cli"             , "0"   , PHP_INI_SYSTEM, OnUpdateBool,              accel_directives.enable_cli,                zend_accel_globals, accel_globals)
307 	STD_PHP_INI_ENTRY("opcache.error_log"                , ""    , PHP_INI_SYSTEM, OnUpdateString,	         accel_directives.error_log,                 zend_accel_globals, accel_globals)
308 	STD_PHP_INI_ENTRY("opcache.restrict_api"             , ""    , PHP_INI_SYSTEM, OnUpdateString,	         accel_directives.restrict_api,              zend_accel_globals, accel_globals)
309 
310 #ifndef ZEND_WIN32
311 	STD_PHP_INI_ENTRY("opcache.lockfile_path"             , "/tmp"    , PHP_INI_SYSTEM, OnUpdateString,           accel_directives.lockfile_path,              zend_accel_globals, accel_globals)
312 #else
313 	STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM,	OnUpdateString,	                             accel_directives.mmap_base,                 zend_accel_globals, accel_globals)
314 #endif
315 
316 #ifdef HAVE_OPCACHE_FILE_CACHE
317 	STD_PHP_INI_ENTRY("opcache.file_cache"                    , NULL  , PHP_INI_SYSTEM, OnUpdateFileCache, accel_directives.file_cache,                    zend_accel_globals, accel_globals)
318 	STD_PHP_INI_BOOLEAN("opcache.file_cache_only"               , "0"   , PHP_INI_SYSTEM, OnUpdateBool,	   accel_directives.file_cache_only,               zend_accel_globals, accel_globals)
319 	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)
320 #endif
321 #if ENABLE_FILE_CACHE_FALLBACK
322 	STD_PHP_INI_BOOLEAN("opcache.file_cache_fallback"           , "1"   , PHP_INI_SYSTEM, OnUpdateBool,	   accel_directives.file_cache_fallback,           zend_accel_globals, accel_globals)
323 #endif
324 #ifdef HAVE_HUGE_CODE_PAGES
325 	STD_PHP_INI_BOOLEAN("opcache.huge_code_pages"             , "0"   , PHP_INI_SYSTEM, OnUpdateBool,      accel_directives.huge_code_pages,               zend_accel_globals, accel_globals)
326 #endif
327 ZEND_INI_END()
328 
329 static int filename_is_in_cache(zend_string *filename)
330 {
331 	char *key;
332 	int key_length;
333 
334 	key = accel_make_persistent_key(ZSTR_VAL(filename), ZSTR_LEN(filename), &key_length);
335 	if (key != NULL) {
336 		zend_persistent_script *persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length);
337 		if (persistent_script && !persistent_script->corrupted) {
338 			zend_file_handle handle = {{0}, NULL, NULL, 0, 0};
339 
340 			handle.filename = ZSTR_VAL(filename);
341 			handle.type = ZEND_HANDLE_FILENAME;
342 
343 			if (ZCG(accel_directives).validate_timestamps) {
344 				return validate_timestamp_and_record_ex(persistent_script, &handle) == SUCCESS;
345 			}
346 
347 			return 1;
348 		}
349 	}
350 
351 	return 0;
352 }
353 
354 static int accel_file_in_cache(INTERNAL_FUNCTION_PARAMETERS)
355 {
356 	zval zfilename;
357 
358 	if (ZEND_NUM_ARGS() != 1 ||
359 	    zend_get_parameters_array_ex(1, &zfilename) == FAILURE ||
360 	    Z_TYPE(zfilename) != IS_STRING ||
361 	    Z_STRLEN(zfilename) == 0) {
362 		return 0;
363 	}
364 	return filename_is_in_cache(Z_STR(zfilename));
365 }
366 
367 static ZEND_NAMED_FUNCTION(accel_file_exists)
368 {
369 	if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
370 		RETURN_TRUE;
371 	} else {
372 		orig_file_exists(INTERNAL_FUNCTION_PARAM_PASSTHRU);
373 	}
374 }
375 
376 static ZEND_NAMED_FUNCTION(accel_is_file)
377 {
378 	if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
379 		RETURN_TRUE;
380 	} else {
381 		orig_is_file(INTERNAL_FUNCTION_PARAM_PASSTHRU);
382 	}
383 }
384 
385 static ZEND_NAMED_FUNCTION(accel_is_readable)
386 {
387 	if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) {
388 		RETURN_TRUE;
389 	} else {
390 		orig_is_readable(INTERNAL_FUNCTION_PARAM_PASSTHRU);
391 	}
392 }
393 
394 static ZEND_MINIT_FUNCTION(zend_accelerator)
395 {
396 	(void)type; /* keep the compiler happy */
397 
398 	REGISTER_INI_ENTRIES();
399 
400 	return SUCCESS;
401 }
402 
403 void zend_accel_override_file_functions(void)
404 {
405 	zend_function *old_function;
406 	if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).file_override_enabled) {
407 #ifdef HAVE_OPCACHE_FILE_CACHE
408 		if (file_cache_only) {
409 			zend_accel_error(ACCEL_LOG_WARNING, "file_override_enabled has no effect when file_cache_only is set");
410 			return;
411 		}
412 #endif
413 		/* override file_exists */
414 		if ((old_function = zend_hash_str_find_ptr(CG(function_table), "file_exists", sizeof("file_exists")-1)) != NULL) {
415 			orig_file_exists = old_function->internal_function.handler;
416 			old_function->internal_function.handler = accel_file_exists;
417 		}
418 		if ((old_function = zend_hash_str_find_ptr(CG(function_table), "is_file", sizeof("is_file")-1)) != NULL) {
419 			orig_is_file = old_function->internal_function.handler;
420 			old_function->internal_function.handler = accel_is_file;
421 		}
422 		if ((old_function = zend_hash_str_find_ptr(CG(function_table), "is_readable", sizeof("is_readable")-1)) != NULL) {
423 			orig_is_readable = old_function->internal_function.handler;
424 			old_function->internal_function.handler = accel_is_readable;
425 		}
426 	}
427 }
428 
429 static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator)
430 {
431 	(void)type; /* keep the compiler happy */
432 
433 	UNREGISTER_INI_ENTRIES();
434 	accel_shutdown();
435 	return SUCCESS;
436 }
437 
438 void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
439 {
440 	php_info_print_table_start();
441 
442 	if (
443 #ifdef HAVE_OPCACHE_FILE_CACHE
444 		(ZCG(accelerator_enabled) || file_cache_only)
445 #else
446 		(ZCG(accelerator_enabled))
447 #endif
448 	) {
449 		php_info_print_table_row(2, "Opcode Caching", "Up and Running");
450 	} else {
451 		php_info_print_table_row(2, "Opcode Caching", "Disabled");
452 	}
453 	if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).optimization_level) {
454 		php_info_print_table_row(2, "Optimization", "Enabled");
455 	} else {
456 		php_info_print_table_row(2, "Optimization", "Disabled");
457 	}
458 #ifdef HAVE_OPCACHE_FILE_CACHE
459 	if (!file_cache_only) {
460 		php_info_print_table_row(2, "SHM Cache", "Enabled");
461 	} else {
462 		php_info_print_table_row(2, "SHM Cache", "Disabled");
463 	}
464 	if (ZCG(accel_directives).file_cache) {
465 		php_info_print_table_row(2, "File Cache", "Enabled");
466 	} else {
467 		php_info_print_table_row(2, "File Cache", "Disabled");
468 	}
469 	if (file_cache_only) {
470 		if (!accel_startup_ok || zps_api_failure_reason) {
471 			php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
472 		} else {
473 			php_info_print_table_row(2, "Startup", "OK");
474 		}
475 	} else
476 #endif
477 	if (ZCG(enabled)) {
478 		if (!accel_startup_ok || zps_api_failure_reason) {
479 			php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
480 		} else {
481 			char buf[32];
482 			php_info_print_table_row(2, "Startup", "OK");
483 			php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model());
484 			snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(hits));
485 			php_info_print_table_row(2, "Cache hits", buf);
486 			snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
487 			php_info_print_table_row(2, "Cache misses", buf);
488 			snprintf(buf, sizeof(buf), ZEND_LONG_FMT, ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
489 			php_info_print_table_row(2, "Used memory", buf);
490 			snprintf(buf, sizeof(buf), "%zu", zend_shared_alloc_get_free_memory());
491 			php_info_print_table_row(2, "Free memory", buf);
492 			snprintf(buf, sizeof(buf), "%zu", ZSMMG(wasted_shared_memory));
493 			php_info_print_table_row(2, "Wasted memory", buf);
494 			if (ZCSG(interned_strings).start && ZCSG(interned_strings).end) {
495 				snprintf(buf, sizeof(buf), "%zu", (size_t)((char*)ZCSG(interned_strings).top - (char*)ZCSG(interned_strings).start));
496 				php_info_print_table_row(2, "Interned Strings Used memory", buf);
497 				snprintf(buf, sizeof(buf), "%zu", (size_t)((char*)ZCSG(interned_strings).end - (char*)ZCSG(interned_strings).top));
498 				php_info_print_table_row(2, "Interned Strings Free memory", buf);
499 			}
500 			snprintf(buf, sizeof(buf), "%" PRIu32, ZCSG(hash).num_direct_entries);
501 			php_info_print_table_row(2, "Cached scripts", buf);
502 			snprintf(buf, sizeof(buf), "%" PRIu32, ZCSG(hash).num_entries);
503 			php_info_print_table_row(2, "Cached keys", buf);
504 			snprintf(buf, sizeof(buf), "%" PRIu32, ZCSG(hash).max_num_entries);
505 			php_info_print_table_row(2, "Max keys", buf);
506 			snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(oom_restarts));
507 			php_info_print_table_row(2, "OOM restarts", buf);
508 			snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(hash_restarts));
509 			php_info_print_table_row(2, "Hash keys restarts", buf);
510 			snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, ZCSG(manual_restarts));
511 			php_info_print_table_row(2, "Manual restarts", buf);
512 		}
513 	}
514 
515 	php_info_print_table_end();
516 	DISPLAY_INI_ENTRIES();
517 }
518 
519 static zend_module_entry accel_module_entry = {
520 	STANDARD_MODULE_HEADER,
521 	ACCELERATOR_PRODUCT_NAME,
522 	accel_functions,
523 	ZEND_MINIT(zend_accelerator),
524 	ZEND_MSHUTDOWN(zend_accelerator),
525 	NULL,
526 	NULL,
527 	zend_accel_info,
528 	PHP_VERSION,
529 	NO_MODULE_GLOBALS,
530 	accel_post_deactivate,
531 	STANDARD_MODULE_PROPERTIES_EX
532 };
533 
534 int start_accel_module(void)
535 {
536 	return zend_startup_module(&accel_module_entry);
537 }
538 
539 /* {{{ proto array accelerator_get_scripts()
540    Get the scripts which are accelerated by ZendAccelerator */
541 static int accelerator_get_scripts(zval *return_value)
542 {
543 	uint32_t i;
544 	zval persistent_script_report;
545 	zend_accel_hash_entry *cache_entry;
546 	struct tm *ta;
547 	struct timeval exec_time;
548 	struct timeval fetch_time;
549 
550 	if (!ZCG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
551 		return 0;
552 	}
553 
554 	array_init(return_value);
555 	for (i = 0; i<ZCSG(hash).max_num_entries; i++) {
556 		for (cache_entry = ZCSG(hash).hash_table[i]; cache_entry; cache_entry = cache_entry->next) {
557 			zend_persistent_script *script;
558 			char *str;
559 			size_t len;
560 
561 			if (cache_entry->indirect) continue;
562 
563 			script = (zend_persistent_script *)cache_entry->data;
564 
565 			array_init(&persistent_script_report);
566 			add_assoc_str(&persistent_script_report, "full_path", zend_string_dup(script->script.filename, 0));
567 			add_assoc_long(&persistent_script_report, "hits", (zend_long)script->dynamic_members.hits);
568 			add_assoc_long(&persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
569 			ta = localtime(&script->dynamic_members.last_used);
570 			str = asctime(ta);
571 			len = strlen(str);
572 			if (len > 0 && str[len - 1] == '\n') len--;
573 			add_assoc_stringl(&persistent_script_report, "last_used", str, len);
574 			add_assoc_long(&persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used);
575 			if (ZCG(accel_directives).validate_timestamps) {
576 				add_assoc_long(&persistent_script_report, "timestamp", (zend_long)script->timestamp);
577 			}
578 			timerclear(&exec_time);
579 			timerclear(&fetch_time);
580 
581 			zend_hash_str_update(Z_ARRVAL_P(return_value), cache_entry->key, cache_entry->key_length, &persistent_script_report);
582 		}
583 	}
584 	accelerator_shm_read_unlock();
585 
586 	return 1;
587 }
588 
589 /* {{{ proto array accelerator_get_status([bool fetch_scripts])
590    Obtain statistics information regarding code acceleration */
591 static ZEND_FUNCTION(opcache_get_status)
592 {
593 	zend_long reqs;
594 	zval memory_usage, statistics, scripts;
595 	zend_bool fetch_scripts = 1;
596 
597 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &fetch_scripts) == FAILURE) {
598 		return;
599 	}
600 
601 	if (!validate_api_restriction()) {
602 		RETURN_FALSE;
603 	}
604 
605 	if (!accel_startup_ok) {
606 		RETURN_FALSE;
607 	}
608 
609 	array_init(return_value);
610 
611 	/* Trivia */
612 	add_assoc_bool(return_value, "opcache_enabled", ZCG(accelerator_enabled));
613 
614 #ifdef HAVE_OPCACHE_FILE_CACHE
615 	if (ZCG(accel_directives).file_cache) {
616 		add_assoc_string(return_value, "file_cache", ZCG(accel_directives).file_cache);
617 	}
618 	if (file_cache_only) {
619 		add_assoc_bool(return_value, "file_cache_only", 1);
620 		return;
621 	}
622 #endif
623 
624 	add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted));
625 	add_assoc_bool(return_value, "restart_pending", ZCSG(restart_pending));
626 	add_assoc_bool(return_value, "restart_in_progress", ZCSG(restart_in_progress));
627 
628 	/* Memory usage statistics */
629 	array_init(&memory_usage);
630 	add_assoc_long(&memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
631 	add_assoc_long(&memory_usage, "free_memory", zend_shared_alloc_get_free_memory());
632 	add_assoc_long(&memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory));
633 	add_assoc_double(&memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0);
634 	add_assoc_zval(return_value, "memory_usage", &memory_usage);
635 
636 	if (ZCSG(interned_strings).start && ZCSG(interned_strings).end) {
637 		zval interned_strings_usage;
638 
639 		array_init(&interned_strings_usage);
640 		add_assoc_long(&interned_strings_usage, "buffer_size", (char*)ZCSG(interned_strings).end - (char*)ZCSG(interned_strings).start);
641 		add_assoc_long(&interned_strings_usage, "used_memory", (char*)ZCSG(interned_strings).top - (char*)ZCSG(interned_strings).start);
642 		add_assoc_long(&interned_strings_usage, "free_memory", (char*)ZCSG(interned_strings).end - (char*)ZCSG(interned_strings).top);
643 		add_assoc_long(&interned_strings_usage, "number_of_strings", ZCSG(interned_strings).nNumOfElements);
644 		add_assoc_zval(return_value, "interned_strings_usage", &interned_strings_usage);
645 	}
646 
647 	/* Accelerator statistics */
648 	array_init(&statistics);
649 	add_assoc_long(&statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries);
650 	add_assoc_long(&statistics, "num_cached_keys",    ZCSG(hash).num_entries);
651 	add_assoc_long(&statistics, "max_cached_keys",    ZCSG(hash).max_num_entries);
652 	add_assoc_long(&statistics, "hits", (zend_long)ZCSG(hits));
653 	add_assoc_long(&statistics, "start_time", ZCSG(start_time));
654 	add_assoc_long(&statistics, "last_restart_time", ZCSG(last_restart_time));
655 	add_assoc_long(&statistics, "oom_restarts", ZCSG(oom_restarts));
656 	add_assoc_long(&statistics, "hash_restarts", ZCSG(hash_restarts));
657 	add_assoc_long(&statistics, "manual_restarts", ZCSG(manual_restarts));
658 	add_assoc_long(&statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
659 	add_assoc_long(&statistics, "blacklist_misses", ZCSG(blacklist_misses));
660 	reqs = ZCSG(hits)+ZCSG(misses);
661 	add_assoc_double(&statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0);
662 	add_assoc_double(&statistics, "opcache_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0);
663 	add_assoc_zval(return_value, "opcache_statistics", &statistics);
664 
665 	if (fetch_scripts) {
666 		/* accelerated scripts */
667 		if (accelerator_get_scripts(&scripts)) {
668 			add_assoc_zval(return_value, "scripts", &scripts);
669 		}
670 	}
671 }
672 
673 static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value)
674 {
675 	add_next_index_stringl(return_value, p->path, p->path_length);
676 	return 0;
677 }
678 
679 /* {{{ proto array accelerator_get_configuration()
680    Obtain configuration information */
681 static ZEND_FUNCTION(opcache_get_configuration)
682 {
683 	zval directives, version, blacklist;
684 
685 	if (zend_parse_parameters_none() == FAILURE) {
686 		RETURN_FALSE;
687 	}
688 
689 	if (!validate_api_restriction()) {
690 		RETURN_FALSE;
691 	}
692 
693 	array_init(return_value);
694 
695 	/* directives */
696 	array_init(&directives);
697 	add_assoc_bool(&directives, "opcache.enable",              ZCG(enabled));
698 	add_assoc_bool(&directives, "opcache.enable_cli",          ZCG(accel_directives).enable_cli);
699 	add_assoc_bool(&directives, "opcache.use_cwd",             ZCG(accel_directives).use_cwd);
700 	add_assoc_bool(&directives, "opcache.validate_timestamps", ZCG(accel_directives).validate_timestamps);
701 	add_assoc_bool(&directives, "opcache.validate_permission", ZCG(accel_directives).validate_permission);
702 #ifndef ZEND_WIN32
703 	add_assoc_bool(&directives, "opcache.validate_root",       ZCG(accel_directives).validate_root);
704 #endif
705 	add_assoc_bool(&directives, "opcache.dups_fix",            ZCG(accel_directives).ignore_dups);
706 	add_assoc_bool(&directives, "opcache.revalidate_path",     ZCG(accel_directives).revalidate_path);
707 
708 	add_assoc_long(&directives,   "opcache.log_verbosity_level",    ZCG(accel_directives).log_verbosity_level);
709 	add_assoc_long(&directives,	 "opcache.memory_consumption",     ZCG(accel_directives).memory_consumption);
710 	add_assoc_long(&directives,	 "opcache.interned_strings_buffer",ZCG(accel_directives).interned_strings_buffer);
711 	add_assoc_long(&directives, 	 "opcache.max_accelerated_files",  ZCG(accel_directives).max_accelerated_files);
712 	add_assoc_double(&directives, "opcache.max_wasted_percentage",  ZCG(accel_directives).max_wasted_percentage);
713 	add_assoc_long(&directives, 	 "opcache.consistency_checks",     ZCG(accel_directives).consistency_checks);
714 	add_assoc_long(&directives, 	 "opcache.force_restart_timeout",  ZCG(accel_directives).force_restart_timeout);
715 	add_assoc_long(&directives, 	 "opcache.revalidate_freq",        ZCG(accel_directives).revalidate_freq);
716 	add_assoc_string(&directives, "opcache.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model));
717 	add_assoc_string(&directives, "opcache.blacklist_filename",     STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename));
718 	add_assoc_long(&directives,   "opcache.max_file_size",          ZCG(accel_directives).max_file_size);
719 	add_assoc_string(&directives, "opcache.error_log",              STRING_NOT_NULL(ZCG(accel_directives).error_log));
720 
721 	add_assoc_bool(&directives,   "opcache.protect_memory",         ZCG(accel_directives).protect_memory);
722 	add_assoc_bool(&directives,   "opcache.save_comments",          ZCG(accel_directives).save_comments);
723 	add_assoc_bool(&directives,   "opcache.enable_file_override",   ZCG(accel_directives).file_override_enabled);
724 	add_assoc_long(&directives, 	 "opcache.optimization_level",     ZCG(accel_directives).optimization_level);
725 
726 #ifndef ZEND_WIN32
727 	add_assoc_string(&directives, "opcache.lockfile_path",          STRING_NOT_NULL(ZCG(accel_directives).lockfile_path));
728 #else
729 	add_assoc_string(&directives, "opcache.mmap_base",              STRING_NOT_NULL(ZCG(accel_directives).mmap_base));
730 #endif
731 
732 #ifdef HAVE_OPCACHE_FILE_CACHE
733 	add_assoc_string(&directives, "opcache.file_cache",                    ZCG(accel_directives).file_cache ? ZCG(accel_directives).file_cache : "");
734 	add_assoc_bool(&directives,   "opcache.file_cache_only",               ZCG(accel_directives).file_cache_only);
735 	add_assoc_bool(&directives,   "opcache.file_cache_consistency_checks", ZCG(accel_directives).file_cache_consistency_checks);
736 #endif
737 #if ENABLE_FILE_CACHE_FALLBACK
738 	add_assoc_bool(&directives,   "opcache.file_cache_fallback",           ZCG(accel_directives).file_cache_fallback);
739 #endif
740 
741 	add_assoc_long(&directives,   "opcache.file_update_protection",  ZCG(accel_directives).file_update_protection);
742 	add_assoc_long(&directives,   "opcache.opt_debug_level",         ZCG(accel_directives).opt_debug_level);
743 	add_assoc_string(&directives, "opcache.restrict_api",            STRING_NOT_NULL(ZCG(accel_directives).restrict_api));
744 #ifdef HAVE_HUGE_CODE_PAGES
745 	add_assoc_bool(&directives,   "opcache.huge_code_pages",         ZCG(accel_directives).huge_code_pages);
746 #endif
747 
748 	add_assoc_zval(return_value, "directives", &directives);
749 
750 	/*version */
751 	array_init(&version);
752 	add_assoc_string(&version, "version", PHP_VERSION);
753 	add_assoc_string(&version, "opcache_product_name", ACCELERATOR_PRODUCT_NAME);
754 	add_assoc_zval(return_value, "version", &version);
755 
756 	/* blacklist */
757 	array_init(&blacklist);
758 	zend_accel_blacklist_apply(&accel_blacklist, add_blacklist_path, &blacklist);
759 	add_assoc_zval(return_value, "blacklist", &blacklist);
760 }
761 
762 /* {{{ proto void accelerator_reset()
763    Request that the contents of the opcode cache to be reset */
764 static ZEND_FUNCTION(opcache_reset)
765 {
766 	if (zend_parse_parameters_none() == FAILURE) {
767 		RETURN_FALSE;
768 	}
769 
770 	if (!validate_api_restriction()) {
771 		RETURN_FALSE;
772 	}
773 
774 	if ((!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled))
775 #if ENABLE_FILE_CACHE_FALLBACK
776 	&& !fallback_process
777 #endif
778 	) {
779 		RETURN_FALSE;
780 	}
781 
782 	/* exclusive lock */
783 	zend_shared_alloc_lock();
784 	zend_accel_schedule_restart(ACCEL_RESTART_USER);
785 	zend_shared_alloc_unlock();
786 	RETURN_TRUE;
787 }
788 
789 /* {{{ proto void opcache_invalidate(string $script [, bool $force = false])
790    Invalidates cached script (in necessary or forced) */
791 static ZEND_FUNCTION(opcache_invalidate)
792 {
793 	char *script_name;
794 	size_t script_name_len;
795 	zend_bool force = 0;
796 
797 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &script_name, &script_name_len, &force) == FAILURE) {
798 		return;
799 	}
800 
801 	if (!validate_api_restriction()) {
802 		RETURN_FALSE;
803 	}
804 
805 	if (zend_accel_invalidate(script_name, script_name_len, force) == SUCCESS) {
806 		RETURN_TRUE;
807 	} else {
808 		RETURN_FALSE;
809 	}
810 }
811 
812 static ZEND_FUNCTION(opcache_compile_file)
813 {
814 	char *script_name;
815 	size_t script_name_len;
816 	zend_file_handle handle;
817 	zend_op_array *op_array = NULL;
818 	zend_execute_data *orig_execute_data = NULL;
819 
820 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &script_name, &script_name_len) == FAILURE) {
821 		return;
822 	}
823 
824 	if (!ZCG(accelerator_enabled)) {
825 		zend_error(E_NOTICE, ACCELERATOR_PRODUCT_NAME " seems to be disabled, can't compile file");
826 		RETURN_FALSE;
827 	}
828 
829 	handle.filename = script_name;
830 	handle.free_filename = 0;
831 	handle.opened_path = NULL;
832 	handle.type = ZEND_HANDLE_FILENAME;
833 
834 	orig_execute_data = EG(current_execute_data);
835 
836 	zend_try {
837 		op_array = persistent_compile_file(&handle, ZEND_INCLUDE);
838 	} zend_catch {
839 		EG(current_execute_data) = orig_execute_data;
840 		zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " could not compile file %s", handle.filename);
841 	} zend_end_try();
842 
843 	if(op_array != NULL) {
844 		destroy_op_array(op_array);
845 		efree(op_array);
846 		RETVAL_TRUE;
847 	} else {
848 		RETVAL_FALSE;
849 	}
850 	zend_destroy_file_handle(&handle);
851 }
852 
853 /* {{{ proto bool opcache_is_script_cached(string $script)
854    Return true if the script is cached in OPCache, false if it is not cached or if OPCache is not running. */
855 static ZEND_FUNCTION(opcache_is_script_cached)
856 {
857 	zend_string *script_name;
858 
859 	if (!validate_api_restriction()) {
860 		RETURN_FALSE;
861 	}
862 
863 	if (!ZCG(accelerator_enabled)) {
864 		RETURN_FALSE;
865 	}
866 
867 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &script_name) == FAILURE) {
868 		return;
869 	}
870 
871 	RETURN_BOOL(filename_is_in_cache(script_name));
872 }
873