1/*
2   +----------------------------------------------------------------------+
3   | Zend OPcache                                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2014 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@zend.com>                                |
16   |          Zeev Suraski <zeev@zend.com>                                |
17   |          Stanislav Malyshev <stas@zend.com>                          |
18   |          Dmitry Stogov <dmitry@zend.com>                             |
19   +----------------------------------------------------------------------+
20*/
21
22#include "main/php.h"
23#include "main/php_globals.h"
24#include "zend.h"
25#include "zend_extensions.h"
26#include "zend_compile.h"
27#include "ZendAccelerator.h"
28#include "zend_persist.h"
29#include "zend_shared_alloc.h"
30#include "zend_accelerator_module.h"
31#include "zend_accelerator_blacklist.h"
32#include "zend_list.h"
33#include "zend_execute.h"
34#include "main/SAPI.h"
35#include "main/php_streams.h"
36#include "main/php_open_temporary_file.h"
37#include "zend_API.h"
38#include "zend_ini.h"
39#if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
40# include "zend_virtual_cwd.h"
41#else
42# include "TSRM/tsrm_virtual_cwd.h"
43#endif
44#include "zend_accelerator_util_funcs.h"
45#include "zend_accelerator_hash.h"
46
47#ifndef ZEND_WIN32
48#include  <netdb.h>
49#endif
50
51#ifdef ZEND_WIN32
52typedef int uid_t;
53typedef int gid_t;
54#include <io.h>
55#endif
56
57#ifndef ZEND_WIN32
58# include <sys/time.h>
59#else
60# include <process.h>
61#endif
62
63#ifdef HAVE_UNISTD_H
64# include <unistd.h>
65#endif
66#include <fcntl.h>
67#include <signal.h>
68#include <time.h>
69
70#ifndef ZEND_WIN32
71# include <sys/types.h>
72# include <sys/ipc.h>
73#endif
74
75#include <sys/stat.h>
76#include <errno.h>
77
78#define SHM_PROTECT() \
79    do { \
80        if (ZCG(accel_directives).protect_memory) { \
81            zend_accel_shared_protect(1 TSRMLS_CC); \
82        } \
83    } while (0)
84#define SHM_UNPROTECT() \
85    do { \
86        if (ZCG(accel_directives).protect_memory) { \
87            zend_accel_shared_protect(0 TSRMLS_CC); \
88        } \
89    } while (0)
90
91ZEND_EXTENSION();
92
93#ifndef ZTS
94zend_accel_globals accel_globals;
95#else
96int accel_globals_id;
97#endif
98
99/* Points to the structure shared across all PHP processes */
100zend_accel_shared_globals *accel_shared_globals = NULL;
101
102/* true globals, no need for thread safety */
103zend_bool accel_startup_ok = 0;
104static char *zps_failure_reason = NULL;
105char *zps_api_failure_reason = NULL;
106
107static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
108static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle  TSRMLS_DC);
109#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
110static char *(*accelerator_orig_zend_resolve_path)(const char *filename, int filename_len TSRMLS_DC);
111#endif
112static void (*orig_chdir)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
113static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL;
114
115#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
116static char *accel_php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC);
117#endif
118
119#ifdef ZEND_WIN32
120# define INCREMENT(v) InterlockedIncrement(&ZCSG(v))
121# define DECREMENT(v) InterlockedDecrement(&ZCSG(v))
122# define LOCKVAL(v)   (ZCSG(v))
123#endif
124
125#ifdef ZEND_WIN32
126static time_t zend_accel_get_time(void)
127{
128    FILETIME now;
129    GetSystemTimeAsFileTime(&now);
130
131    return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000);
132}
133#else
134# define zend_accel_get_time() time(NULL)
135#endif
136
137static inline int is_stream_path(const char *filename)
138{
139    const char *p;
140
141    for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++);
142    return ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/'));
143}
144
145static inline int is_cacheable_stream_path(const char *filename)
146{
147    return memcmp(filename, "file://", sizeof("file://") - 1) == 0 ||
148           memcmp(filename, "phar://", sizeof("phar://") - 1) == 0;
149}
150
151/* O+ overrides PHP chdir() function and remembers the current working directory
152 * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and
153 * avoid getcwd() call.
154 */
155static ZEND_FUNCTION(accel_chdir)
156{
157    char cwd[MAXPATHLEN];
158
159    orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU);
160    if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
161        if (ZCG(cwd)) {
162            efree(ZCG(cwd));
163        }
164        ZCG(cwd_len) = strlen(cwd);
165        ZCG(cwd) = estrndup(cwd, ZCG(cwd_len));
166    } else {
167        if (ZCG(cwd)) {
168            efree(ZCG(cwd));
169            ZCG(cwd) = NULL;
170        }
171    }
172}
173
174static inline char* accel_getcwd(int *cwd_len TSRMLS_DC)
175{
176    if (ZCG(cwd)) {
177        *cwd_len = ZCG(cwd_len);
178        return ZCG(cwd);
179    } else {
180        char cwd[MAXPATHLEN + 1];
181
182        if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
183            return NULL;
184        }
185        *cwd_len = ZCG(cwd_len) = strlen(cwd);
186        ZCG(cwd) = estrndup(cwd, ZCG(cwd_len));
187        return ZCG(cwd);
188    }
189}
190
191void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason TSRMLS_DC)
192{
193    if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) {
194        zend_accel_schedule_restart(reason TSRMLS_CC);
195    }
196}
197
198/* O+ tracks changes of "include_path" directive. It stores all the requested
199 * values in ZCG(include_paths) shared hash table, current value in
200 * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in
201 * ZCG(include_path_key).
202 */
203static ZEND_INI_MH(accel_include_path_on_modify)
204{
205    int ret = orig_include_path_on_modify(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
206
207    ZCG(include_path_key) = NULL;
208    if (ret == SUCCESS) {
209        ZCG(include_path) = new_value;
210        if (ZCG(include_path) && *ZCG(include_path)) {
211            ZCG(include_path_len) = new_value_length;
212
213            if (ZCG(enabled) && accel_startup_ok &&
214                (ZCG(counted) || ZCSG(accelerator_enabled))) {
215
216                ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1);
217                if (!ZCG(include_path_key) &&
218                    !zend_accel_hash_is_full(&ZCSG(include_paths))) {
219                    SHM_UNPROTECT();
220                    zend_shared_alloc_lock(TSRMLS_C);
221
222                    ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1);
223                    if (!ZCG(include_path_key) &&
224                        !zend_accel_hash_is_full(&ZCSG(include_paths))) {
225                        char *key;
226
227                        key = zend_shared_alloc(ZCG(include_path_len) + 2);
228                        if (key) {
229                            memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
230                            key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
231                            ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
232                            zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key));
233                        } else {
234                            zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
235                        }
236                    }
237
238                    zend_shared_alloc_unlock(TSRMLS_C);
239                    SHM_PROTECT();
240                }
241            } else {
242                ZCG(include_path_check) = 1;
243            }
244        } else {
245            ZCG(include_path) = "";
246            ZCG(include_path_len) = 0;
247        }
248    }
249    return ret;
250}
251
252#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
253/* Interned strings support */
254static char *orig_interned_strings_start;
255static char *orig_interned_strings_end;
256static const char *(*orig_new_interned_string)(const char *str, int len, int free_src TSRMLS_DC);
257static void (*orig_interned_strings_snapshot)(TSRMLS_D);
258static void (*orig_interned_strings_restore)(TSRMLS_D);
259
260/* O+ disables creation of interned strings by regular PHP compiler, instead,
261 * it creates interned strings in shared memory when saves a script.
262 * Such interned strings are shared across all PHP processes
263 */
264static const char *accel_new_interned_string_for_php(const char *str, int len, int free_src TSRMLS_DC)
265{
266    return str;
267}
268
269static void accel_interned_strings_snapshot_for_php(TSRMLS_D)
270{
271}
272
273static void accel_interned_strings_restore_for_php(TSRMLS_D)
274{
275}
276
277#ifndef ZTS
278static void accel_interned_strings_restore_state(TSRMLS_D)
279{
280    unsigned int i;
281
282    for (i = 0; i < ZCSG(interned_strings).nTableSize; i++) {
283        ZCSG(interned_strings).arBuckets[i] = ZCSG(interned_strings_saved_state).arBuckets[i];
284        if (ZCSG(interned_strings).arBuckets[i]) {
285            ZCSG(interned_strings).arBuckets[i]->pLast = NULL;
286        }
287    }
288    ZCSG(interned_strings).pListHead = ZCSG(interned_strings_saved_state).pListHead;
289    ZCSG(interned_strings).pListTail = ZCSG(interned_strings_saved_state).pListTail;
290    if (ZCSG(interned_strings).pListHead) {
291        ZCSG(interned_strings).pListHead->pListLast = NULL;
292    }
293    if (ZCSG(interned_strings).pListTail) {
294        ZCSG(interned_strings).pListTail->pListNext = NULL;
295    }
296    ZCSG(interned_strings_top) = ZCSG(interned_strings_saved_state).top;
297}
298
299static void accel_interned_strings_save_state(TSRMLS_D)
300{
301    ZCSG(interned_strings_saved_state).arBuckets = (Bucket**)zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *));
302    if (!ZCSG(interned_strings_saved_state).arBuckets) {
303        zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
304    }
305    memcpy(ZCSG(interned_strings_saved_state).arBuckets, ZCSG(interned_strings).arBuckets, ZCSG(interned_strings).nTableSize * sizeof(Bucket *));
306    ZCSG(interned_strings_saved_state).pListHead = ZCSG(interned_strings).pListHead;
307    ZCSG(interned_strings_saved_state).pListTail = ZCSG(interned_strings).pListTail;
308    ZCSG(interned_strings_saved_state).top = ZCSG(interned_strings_top);
309}
310#endif
311
312const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC)
313{
314/* for now interned strings are supported only for non-ZTS build */
315#ifndef ZTS
316    ulong h;
317    uint nIndex;
318    Bucket *p;
319
320    if (arKey >= ZCSG(interned_strings_start) && arKey < ZCSG(interned_strings_end)) {
321        /* this is already an interned string */
322        return arKey;
323    }
324
325    h = zend_inline_hash_func(arKey, nKeyLength);
326    nIndex = h & ZCSG(interned_strings).nTableMask;
327
328    /* check for existing interned string */
329    p = ZCSG(interned_strings).arBuckets[nIndex];
330    while (p != NULL) {
331        if ((p->h == h) && (p->nKeyLength == (uint)nKeyLength)) {
332            if (!memcmp(p->arKey, arKey, nKeyLength)) {
333                if (free_src) {
334                    efree((char*)arKey);
335                }
336                return p->arKey;
337            }
338        }
339        p = p->pNext;
340    }
341
342    if (ZCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >=
343        ZCSG(interned_strings_end)) {
344        /* no memory, return the same non-interned string */
345        zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
346        return arKey;
347    }
348
349    /* create new interning string in shared interned strings buffer */
350    p = (Bucket *) ZCSG(interned_strings_top);
351    ZCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength);
352
353    p->arKey = (char*)(p + 1);
354    memcpy((char*)p->arKey, arKey, nKeyLength);
355    p->nKeyLength = nKeyLength;
356    p->h = h;
357    p->pData = &p->pDataPtr;
358    p->pDataPtr = p;
359
360    p->pNext = ZCSG(interned_strings).arBuckets[nIndex];
361    p->pLast = NULL;
362    if (p->pNext) {
363        p->pNext->pLast = p;
364    }
365    ZCSG(interned_strings).arBuckets[nIndex] = p;
366
367    p->pListLast = ZCSG(interned_strings).pListTail;
368    ZCSG(interned_strings).pListTail = p;
369    p->pListNext = NULL;
370    if (p->pListLast != NULL) {
371        p->pListLast->pListNext = p;
372    }
373    if (!ZCSG(interned_strings).pListHead) {
374        ZCSG(interned_strings).pListHead = p;
375    }
376
377    ZCSG(interned_strings).nNumOfElements++;
378
379    if (free_src) {
380        efree((char*)arKey);
381    }
382
383    return p->arKey;
384#else
385    return arKey;
386#endif
387}
388
389#ifndef ZTS
390/* Copy PHP interned strings from PHP process memory into the shared memory */
391static void accel_use_shm_interned_strings(TSRMLS_D)
392{
393    Bucket *p, *q;
394
395#if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO
396    /* empty string */
397    CG(interned_empty_string) = accel_new_interned_string("", sizeof(""), 0 TSRMLS_CC);
398#endif
399
400    /* function table hash keys */
401    p = CG(function_table)->pListHead;
402    while (p) {
403        if (p->nKeyLength) {
404            p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
405        }
406        p = p->pListNext;
407    }
408
409    /* class table hash keys, class names, properties, methods, constants, etc */
410    p = CG(class_table)->pListHead;
411    while (p) {
412        zend_class_entry *ce = (zend_class_entry*)(p->pDataPtr);
413
414        if (p->nKeyLength) {
415            p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
416        }
417
418        if (ce->name) {
419            ce->name = accel_new_interned_string(ce->name, ce->name_length + 1, 0 TSRMLS_CC);
420        }
421
422        q = ce->properties_info.pListHead;
423        while (q) {
424            zend_property_info *info = (zend_property_info*)(q->pData);
425
426            if (q->nKeyLength) {
427                q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC);
428            }
429
430            if (info->name) {
431                info->name = accel_new_interned_string(info->name, info->name_length + 1, 0 TSRMLS_CC);
432            }
433
434            q = q->pListNext;
435        }
436
437        q = ce->function_table.pListHead;
438        while (q) {
439            if (q->nKeyLength) {
440                q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC);
441            }
442            q = q->pListNext;
443        }
444
445        q = ce->constants_table.pListHead;
446        while (q) {
447            if (q->nKeyLength) {
448                q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC);
449            }
450            q = q->pListNext;
451        }
452
453        p = p->pListNext;
454    }
455
456    /* constant hash keys */
457    p = EG(zend_constants)->pListHead;
458    while (p) {
459        if (p->nKeyLength) {
460            p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
461        }
462        p = p->pListNext;
463    }
464
465    /* auto globals hash keys and names */
466    p = CG(auto_globals)->pListHead;
467    while (p) {
468        zend_auto_global *auto_global = (zend_auto_global*)p->pData;
469
470        auto_global->name = accel_new_interned_string(auto_global->name, auto_global->name_len + 1, 0 TSRMLS_CC);
471        if (p->nKeyLength) {
472            p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
473        }
474        p = p->pListNext;
475    }
476}
477#endif
478#endif
479
480static inline void accel_restart_enter(TSRMLS_D)
481{
482#ifdef ZEND_WIN32
483    INCREMENT(restart_in);
484#else
485    static const FLOCK_STRUCTURE(restart_in_progress, F_WRLCK, SEEK_SET, 2, 1);
486
487    if (fcntl(lock_file, F_SETLK, &restart_in_progress) == -1) {
488        zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1):  %s (%d)", strerror(errno), errno);
489    }
490#endif
491    ZCSG(restart_in_progress) = 1;
492}
493
494static inline void accel_restart_leave(TSRMLS_D)
495{
496#ifdef ZEND_WIN32
497    ZCSG(restart_in_progress) = 0;
498    DECREMENT(restart_in);
499#else
500    static const FLOCK_STRUCTURE(restart_finished, F_UNLCK, SEEK_SET, 2, 1);
501
502    ZCSG(restart_in_progress) = 0;
503    if (fcntl(lock_file, F_SETLK, &restart_finished) == -1) {
504        zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1):  %s (%d)", strerror(errno), errno);
505    }
506#endif
507}
508
509static inline int accel_restart_is_active(TSRMLS_D)
510{
511    if (ZCSG(restart_in_progress)) {
512#ifndef ZEND_WIN32
513        FLOCK_STRUCTURE(restart_check, F_WRLCK, SEEK_SET, 2, 1);
514
515        if (fcntl(lock_file, F_GETLK, &restart_check) == -1) {
516            zend_accel_error(ACCEL_LOG_DEBUG, "RestartC:  %s (%d)", strerror(errno), errno);
517            return FAILURE;
518        }
519        if (restart_check.l_type == F_UNLCK) {
520            ZCSG(restart_in_progress) = 0;
521            return 0;
522        } else {
523            return 1;
524        }
525#else
526        return LOCKVAL(restart_in) != 0;
527#endif
528    }
529    return 0;
530}
531
532/* Creates a read lock for SHM access */
533static inline void accel_activate_add(TSRMLS_D)
534{
535#ifdef ZEND_WIN32
536    INCREMENT(mem_usage);
537#else
538    static const FLOCK_STRUCTURE(mem_usage_lock, F_RDLCK, SEEK_SET, 1, 1);
539
540    if (fcntl(lock_file, F_SETLK, &mem_usage_lock) == -1) {
541        zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1):  %s (%d)", strerror(errno), errno);
542    }
543#endif
544}
545
546/* Releases a lock for SHM access */
547static inline void accel_deactivate_sub(TSRMLS_D)
548{
549#ifdef ZEND_WIN32
550    if (ZCG(counted)) {
551        DECREMENT(mem_usage);
552        ZCG(counted) = 0;
553    }
554#else
555    static const FLOCK_STRUCTURE(mem_usage_unlock, F_UNLCK, SEEK_SET, 1, 1);
556
557    if (fcntl(lock_file, F_SETLK, &mem_usage_unlock) == -1) {
558        zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1):  %s (%d)", strerror(errno), errno);
559    }
560#endif
561}
562
563static inline void accel_unlock_all(TSRMLS_D)
564{
565#ifdef ZEND_WIN32
566    accel_deactivate_sub(TSRMLS_C);
567#else
568    static const FLOCK_STRUCTURE(mem_usage_unlock_all, F_UNLCK, SEEK_SET, 0, 0);
569
570    if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all) == -1) {
571        zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll:  %s (%d)", strerror(errno), errno);
572    }
573#endif
574}
575
576#ifndef ZEND_WIN32
577static inline void kill_all_lockers(struct flock *mem_usage_check)
578{
579    int tries = 10;
580
581    /* so that other process won't try to force while we are busy cleaning up */
582    ZCSG(force_restart_time) = 0;
583    while (mem_usage_check->l_pid > 0) {
584        while (tries--) {
585            zend_accel_error(ACCEL_LOG_INFO, "Killed locker %d", mem_usage_check->l_pid);
586            if (kill(mem_usage_check->l_pid, SIGKILL)) {
587                break;
588            }
589            /* give it a chance to die */
590            usleep(20000);
591            if (kill(mem_usage_check->l_pid, 0)) {
592                /* can't kill it */
593                break;
594            }
595            usleep(10000);
596        }
597        if (!tries) {
598            zend_accel_error(ACCEL_LOG_INFO, "Can't kill %d after 20 tries!", mem_usage_check->l_pid);
599            ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */
600        }
601
602        mem_usage_check->l_type = F_WRLCK;
603        mem_usage_check->l_whence = SEEK_SET;
604        mem_usage_check->l_start = 1;
605        mem_usage_check->l_len = 1;
606        mem_usage_check->l_pid = -1;
607        if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) {
608            zend_accel_error(ACCEL_LOG_DEBUG, "KLockers:  %s (%d)", strerror(errno), errno);
609            break;
610        }
611
612        if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) {
613            break;
614        }
615    }
616}
617#endif
618
619static inline int accel_is_inactive(TSRMLS_D)
620{
621#ifdef ZEND_WIN32
622    if (LOCKVAL(mem_usage) == 0) {
623        return SUCCESS;
624    }
625#else
626    FLOCK_STRUCTURE(mem_usage_check, F_WRLCK, SEEK_SET, 1, 1);
627
628    mem_usage_check.l_pid = -1;
629    if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) {
630        zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC:  %s (%d)", strerror(errno), errno);
631        return FAILURE;
632    }
633    if (mem_usage_check.l_type == F_UNLCK) {
634        return SUCCESS;
635    }
636
637    if (ZCG(accel_directives).force_restart_timeout
638        && ZCSG(force_restart_time)
639        && time(NULL) >= ZCSG(force_restart_time)) {
640        zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %d (after %d seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
641        kill_all_lockers(&mem_usage_check);
642
643        return FAILURE; /* next request should be able to restart it */
644    }
645#endif
646
647    return FAILURE;
648}
649
650static int zend_get_stream_timestamp(const char *filename, struct stat *statbuf TSRMLS_DC)
651{
652    php_stream_wrapper *wrapper;
653    php_stream_statbuf stream_statbuf;
654    int ret, er;
655
656    if (!filename) {
657        return FAILURE;
658    }
659
660    wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC);
661    if (!wrapper) {
662        return FAILURE;
663    }
664    if (!wrapper->wops || !wrapper->wops->url_stat) {
665        statbuf->st_mtime = 1;
666        return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */
667    }
668
669    er = EG(error_reporting);
670    EG(error_reporting) = 0;
671    zend_try {
672        ret = wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL TSRMLS_CC);
673    } zend_catch {
674        ret = -1;
675    } zend_end_try();
676    EG(error_reporting) = er;
677
678    if (ret != 0) {
679        return FAILURE;
680    }
681
682    *statbuf = stream_statbuf.sb;
683    return SUCCESS;
684}
685
686#if ZEND_WIN32
687static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle, size_t *size)
688{
689    static unsigned __int64 utc_base = 0;
690    static FILETIME utc_base_ft;
691    WIN32_FILE_ATTRIBUTE_DATA fdata;
692
693    if (!file_handle->opened_path) {
694        return 0;
695    }
696
697    if (!utc_base) {
698        SYSTEMTIME st;
699
700        st.wYear = 1970;
701        st.wMonth = 1;
702        st.wDay = 1;
703        st.wHour = 0;
704        st.wMinute = 0;
705        st.wSecond = 0;
706        st.wMilliseconds = 0;
707
708        SystemTimeToFileTime (&st, &utc_base_ft);
709        utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime;
710    }
711
712    if (GetFileAttributesEx(file_handle->opened_path, GetFileExInfoStandard, &fdata) != 0) {
713        unsigned __int64 ftime;
714
715        if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0) {
716            return 0;
717        }
718
719        ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base;
720        ftime /= 10000000L;
721
722        if (size) {
723            *size = (size_t)(((unsigned __int64)fdata.nFileSizeHigh) << 32 + (unsigned __int64)fdata.nFileSizeLow);
724        }
725        return (accel_time_t)ftime;
726    }
727    return 0;
728}
729#endif
730
731static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size TSRMLS_DC)
732{
733    struct stat statbuf;
734#ifdef ZEND_WIN32
735    accel_time_t res;
736#endif
737
738    if (sapi_module.get_stat &&
739        !EG(opline_ptr) &&
740        file_handle->filename == SG(request_info).path_translated) {
741
742        struct stat *tmpbuf = sapi_module.get_stat(TSRMLS_C);
743
744        if (tmpbuf) {
745            if (size) {
746                *size = tmpbuf->st_size;
747            }
748            return tmpbuf->st_mtime;
749        }
750    }
751
752#ifdef ZEND_WIN32
753    res = zend_get_file_handle_timestamp_win(file_handle, size);
754    if (res) {
755        return res;
756    }
757#endif
758
759    switch (file_handle->type) {
760        case ZEND_HANDLE_FD:
761            if (fstat(file_handle->handle.fd, &statbuf) == -1) {
762                return 0;
763            }
764            break;
765        case ZEND_HANDLE_FP:
766            if (fstat(fileno(file_handle->handle.fp), &statbuf) == -1) {
767                if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) {
768                    return 0;
769                }
770            }
771            break;
772        case ZEND_HANDLE_FILENAME:
773#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
774        case ZEND_HANDLE_MAPPED:
775#endif
776            {
777                char *file_path = file_handle->opened_path;
778
779                if (file_path) {
780                    if (is_stream_path(file_path)) {
781                        if (zend_get_stream_timestamp(file_path, &statbuf TSRMLS_CC) == SUCCESS) {
782                            break;
783                        }
784                    }
785                    if (VCWD_STAT(file_path, &statbuf) != -1) {
786                        break;
787                    }
788                }
789
790                if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) {
791                    return 0;
792                }
793                break;
794            }
795        case ZEND_HANDLE_STREAM:
796            {
797                php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
798                php_stream_statbuf sb;
799                int ret, er;
800
801                if (!stream ||
802                    !stream->ops ||
803                    !stream->ops->stat) {
804                    return 0;
805                }
806
807                er = EG(error_reporting);
808                EG(error_reporting) = 0;
809                zend_try {
810                    ret = stream->ops->stat(stream, &sb TSRMLS_CC);
811                } zend_catch {
812                    ret = -1;
813                } zend_end_try();
814                EG(error_reporting) = er;
815                if (ret != 0) {
816                    return 0;
817                }
818
819                statbuf = sb.sb;
820            }
821            break;
822
823        default:
824            return 0;
825    }
826
827    if (size) {
828        *size = statbuf.st_size;
829    }
830    return statbuf.st_mtime;
831}
832
833static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC)
834{
835    zend_file_handle ps_handle;
836    char *full_path_ptr = NULL;
837
838    /** check that the persistant script is indeed the same file we cached
839     * (if part of the path is a symlink than it possible that the user will change it)
840     * See bug #15140
841     */
842    if (file_handle->opened_path) {
843        if (strcmp(persistent_script->full_path, file_handle->opened_path) != 0) {
844            return FAILURE;
845        }
846    } else {
847#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
848        full_path_ptr = accel_php_resolve_path(file_handle->filename, strlen(file_handle->filename), ZCG(include_path) TSRMLS_CC);
849#else
850        full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename) TSRMLS_CC);
851#endif
852        if (full_path_ptr && strcmp(persistent_script->full_path, full_path_ptr) != 0) {
853            efree(full_path_ptr);
854            return FAILURE;
855        }
856        file_handle->opened_path = full_path_ptr;
857    }
858
859    if (persistent_script->timestamp == 0) {
860        if (full_path_ptr) {
861            efree(full_path_ptr);
862            file_handle->opened_path = NULL;
863        }
864        return FAILURE;
865    }
866
867    if (zend_get_file_handle_timestamp(file_handle, NULL TSRMLS_CC) == persistent_script->timestamp) {
868        if (full_path_ptr) {
869            efree(full_path_ptr);
870            file_handle->opened_path = NULL;
871        }
872        return SUCCESS;
873    }
874    if (full_path_ptr) {
875        efree(full_path_ptr);
876        file_handle->opened_path = NULL;
877    }
878
879    ps_handle.type = ZEND_HANDLE_FILENAME;
880    ps_handle.filename = persistent_script->full_path;
881    ps_handle.opened_path = persistent_script->full_path;
882
883    if (zend_get_file_handle_timestamp(&ps_handle, NULL TSRMLS_CC) == persistent_script->timestamp) {
884        return SUCCESS;
885    }
886
887    return FAILURE;
888}
889
890int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC)
891{
892    if (ZCG(accel_directives).revalidate_freq &&
893        (persistent_script->dynamic_members.revalidate >= ZCSG(revalidate_at))) {
894        return SUCCESS;
895    } else if (do_validate_timestamps(persistent_script, file_handle TSRMLS_CC) == FAILURE) {
896        return FAILURE;
897    } else {
898        persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at);
899        return SUCCESS;
900    }
901}
902
903static unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script)
904{
905    signed char *mem = (signed char*)persistent_script->mem;
906    size_t size = persistent_script->size;
907    size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script;
908    unsigned int checksum = ADLER32_INIT;
909
910    if (mem < (signed char*)persistent_script) {
911        checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem);
912        size -= (signed char*)persistent_script - mem;
913        mem  += (signed char*)persistent_script - mem;
914    }
915
916    zend_adler32(checksum, mem, persistent_script_check_block_size);
917    mem  += sizeof(*persistent_script);
918    size -= sizeof(*persistent_script);
919
920    if (size > 0) {
921        checksum = zend_adler32(checksum, mem, size);
922    }
923    return checksum;
924}
925
926/* Instead of resolving full real path name each time we need to identify file,
927 * we create a key that consist from requested file name, current working
928 * directory, current include_path, etc */
929char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC)
930{
931    int key_length;
932
933    /* CWD and include_path don't matter for absolute file names and streams */
934    if (ZCG(accel_directives).use_cwd &&
935        !IS_ABSOLUTE_PATH(file_handle->filename, path_length) &&
936        !is_stream_path(file_handle->filename)) {
937        char *include_path = NULL;
938        int include_path_len = 0;
939        const char *parent_script = NULL;
940        int parent_script_len = 0;
941        int cur_len = 0;
942        int cwd_len;
943        char *cwd;
944
945        if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) == NULL) {
946            /* we don't handle this well for now. */
947            zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set opcache.use_cwd to 0 in ini file", file_handle->filename, errno);
948            if (file_handle->opened_path) {
949                cwd = file_handle->opened_path;
950                cwd_len = strlen(cwd);
951            } else {
952                ZCG(key_len) = 0;
953                return NULL;
954            }
955        }
956
957        if (ZCG(include_path_key)) {
958            include_path = ZCG(include_path_key);
959            include_path_len = 1;
960        } else {
961            include_path = ZCG(include_path);
962            include_path_len = ZCG(include_path_len);
963            if (ZCG(include_path_check) &&
964                ZCG(enabled) && accel_startup_ok &&
965                (ZCG(counted) || ZCSG(accelerator_enabled)) &&
966                !zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1) &&
967                !zend_accel_hash_is_full(&ZCSG(include_paths))) {
968
969                SHM_UNPROTECT();
970                zend_shared_alloc_lock(TSRMLS_C);
971
972                ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1);
973                if (ZCG(include_path_key)) {
974                    include_path = ZCG(include_path_key);
975                    include_path_len = 1;
976                } else if (!zend_accel_hash_is_full(&ZCSG(include_paths))) {
977                    char *key;
978
979                    key = zend_shared_alloc(ZCG(include_path_len) + 2);
980                    if (key) {
981                        memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
982                        key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
983                        ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
984                        zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key));
985                        include_path = ZCG(include_path_key);
986                        include_path_len = 1;
987                    } else {
988                        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
989                    }
990                }
991
992                zend_shared_alloc_unlock(TSRMLS_C);
993                SHM_PROTECT();
994            }
995        }
996
997        /* Here we add to the key the parent script directory,
998           since fopen_wrappers from version 4.0.7 use current script's path
999           in include path too.
1000        */
1001        if (EG(in_execution) &&
1002            (parent_script = zend_get_executed_filename(TSRMLS_C)) != NULL &&
1003            parent_script[0] != '[') {
1004
1005            parent_script_len = strlen(parent_script);
1006            while ((--parent_script_len > 0) && !IS_SLASH(parent_script[parent_script_len]));
1007        }
1008
1009        /* Calculate key length */
1010        key_length = cwd_len + path_length + include_path_len + 2;
1011        if (parent_script_len) {
1012            key_length += parent_script_len + 1;
1013        }
1014
1015        /* Generate key
1016         * Note - the include_path must be the last element in the key,
1017         * since in itself, it may include colons (which we use to separate
1018         * different components of the key)
1019         */
1020        if ((size_t)key_length >= sizeof(ZCG(key))) {
1021            ZCG(key_len) = 0;
1022            return NULL;
1023        }
1024        memcpy(ZCG(key), cwd, cwd_len);
1025        ZCG(key)[cwd_len] = ':';
1026
1027        memcpy(ZCG(key) + cwd_len + 1, file_handle->filename, path_length);
1028
1029        ZCG(key)[cwd_len + 1 + path_length] = ':';
1030
1031        cur_len = cwd_len + 1 + path_length + 1;
1032
1033        if (parent_script_len) {
1034            memcpy(ZCG(key) + cur_len, parent_script, parent_script_len);
1035            cur_len += parent_script_len;
1036            ZCG(key)[cur_len] = ':';
1037            cur_len++;
1038        }
1039        memcpy(ZCG(key) + cur_len, include_path, include_path_len);
1040        ZCG(key)[key_length] = '\0';
1041    } else {
1042        /* not use_cwd */
1043        key_length = path_length;
1044        if ((size_t)key_length >= sizeof(ZCG(key))) {
1045            ZCG(key_len) = 0;
1046            return NULL;
1047        }
1048        memcpy(ZCG(key), file_handle->filename, key_length + 1);
1049    }
1050
1051    *key_len = ZCG(key_len) = key_length;
1052    return ZCG(key);
1053}
1054
1055static inline char *accel_make_persistent_key(zend_file_handle *file_handle, int *key_len TSRMLS_DC)
1056{
1057    return accel_make_persistent_key_ex(file_handle, strlen(file_handle->filename), key_len TSRMLS_CC);
1058}
1059
1060int zend_accel_invalidate(const char *filename, int filename_len, zend_bool force TSRMLS_DC)
1061{
1062    char *realpath;
1063    zend_persistent_script *persistent_script;
1064
1065    if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
1066        return FAILURE;
1067    }
1068
1069#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1070    realpath = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
1071#else
1072    realpath = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
1073#endif
1074
1075    if (!realpath) {
1076        return FAILURE;
1077    }
1078
1079    persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath, strlen(realpath) + 1);
1080    if (persistent_script && !persistent_script->corrupted) {
1081        zend_file_handle file_handle;
1082
1083        file_handle.type = ZEND_HANDLE_FILENAME;
1084        file_handle.filename = realpath;
1085        file_handle.opened_path = realpath;
1086
1087        if (force ||
1088            !ZCG(accel_directives).validate_timestamps ||
1089            do_validate_timestamps(persistent_script, &file_handle TSRMLS_CC) == FAILURE) {
1090            SHM_UNPROTECT();
1091            zend_shared_alloc_lock(TSRMLS_C);
1092            if (!persistent_script->corrupted) {
1093                persistent_script->corrupted = 1;
1094                persistent_script->timestamp = 0;
1095                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1096                if (ZSMMG(memory_exhausted)) {
1097                    zend_accel_restart_reason reason =
1098                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1099                    zend_accel_schedule_restart_if_necessary(reason TSRMLS_CC);
1100                }
1101            }
1102            zend_shared_alloc_unlock(TSRMLS_C);
1103            SHM_PROTECT();
1104        }
1105    }
1106
1107    accelerator_shm_read_unlock(TSRMLS_C);
1108    efree(realpath);
1109
1110    return SUCCESS;
1111}
1112
1113/* Adds another key for existing cached script */
1114static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC)
1115{
1116    if (!zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) {
1117        if (zend_accel_hash_is_full(&ZCSG(hash))) {
1118            zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1119            ZSMMG(memory_exhausted) = 1;
1120            zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH TSRMLS_CC);
1121        } else {
1122            char *new_key = zend_shared_alloc(key_length + 1);
1123            if (new_key) {
1124                memcpy(new_key, key, key_length + 1);
1125                if (zend_accel_hash_update(&ZCSG(hash), new_key, key_length + 1, 1, bucket)) {
1126                    zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", new_key);
1127                }
1128            } else {
1129                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
1130            }
1131        }
1132    }
1133}
1134
1135static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory TSRMLS_DC)
1136{
1137    zend_accel_hash_entry *bucket;
1138    uint memory_used;
1139
1140    /* Check if script may be stored in shared memory */
1141    if (!zend_accel_script_persistable(new_persistent_script)) {
1142        return new_persistent_script;
1143    }
1144
1145    if (!zend_accel_script_optimize(new_persistent_script TSRMLS_CC)) {
1146        return new_persistent_script;
1147    }
1148
1149    if (!compact_persistent_script(new_persistent_script)) {
1150        return new_persistent_script;
1151    }
1152
1153    /* exclusive lock */
1154    zend_shared_alloc_lock(TSRMLS_C);
1155
1156    if (zend_accel_hash_is_full(&ZCSG(hash))) {
1157        zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1158        ZSMMG(memory_exhausted) = 1;
1159        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH TSRMLS_CC);
1160        zend_shared_alloc_unlock(TSRMLS_C);
1161        return new_persistent_script;
1162    }
1163
1164    /* Check if we still need to put the file into the cache (may be it was
1165     * already stored by another process. This final check is done under
1166     * exclusive lock) */
1167    bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
1168    if (bucket) {
1169        zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1170
1171        if (!existing_persistent_script->corrupted) {
1172            if (!ZCG(accel_directives).revalidate_path &&
1173                (!ZCG(accel_directives).validate_timestamps ||
1174                 (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
1175                zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
1176            }
1177            zend_shared_alloc_unlock(TSRMLS_C);
1178            return new_persistent_script;
1179        }
1180    }
1181
1182    /* Calculate the required memory size */
1183    memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length TSRMLS_CC);
1184
1185    /* Allocate shared memory */
1186    ZCG(mem) = zend_shared_alloc(memory_used);
1187    if (!ZCG(mem)) {
1188        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
1189        zend_shared_alloc_unlock(TSRMLS_C);
1190        return new_persistent_script;
1191    }
1192
1193    /* cleanup after calculation */
1194    new_persistent_script->mem  = ZCG(mem);
1195    new_persistent_script->size = memory_used;
1196
1197    /* Copy into shared memory */
1198    new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length TSRMLS_CC);
1199
1200    /* Consistency check */
1201    if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1202        zend_accel_error(
1203            ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1204            "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
1205            new_persistent_script->full_path,
1206            new_persistent_script->mem,
1207            (char *)new_persistent_script->mem + new_persistent_script->size,
1208            ZCG(mem));
1209    }
1210
1211    new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1212
1213    /* store script structure in the hash table */
1214    bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len + 1, 0, new_persistent_script);
1215    if (bucket) {
1216        zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", new_persistent_script->full_path);
1217        if (!ZCG(accel_directives).revalidate_path &&
1218            /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1219            memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
1220            (new_persistent_script->full_path_len != key_length ||
1221             memcmp(new_persistent_script->full_path, key, key_length) != 0)) {
1222            /* link key to the same persistent script in hash table */
1223            if (zend_accel_hash_update(&ZCSG(hash), key, key_length + 1, 1, bucket)) {
1224                zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
1225            } else {
1226                zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1227                ZSMMG(memory_exhausted) = 1;
1228                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH TSRMLS_CC);
1229            }
1230        }
1231    }
1232
1233    new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1234
1235    zend_shared_alloc_unlock(TSRMLS_C);
1236
1237    *from_shared_memory = 1;
1238    return new_persistent_script;
1239}
1240
1241static const struct jit_auto_global_info
1242{
1243    const char *name;
1244    size_t len;
1245} jit_auto_globals_info[] = {
1246    { "_SERVER",  sizeof("_SERVER")},
1247    { "_ENV",     sizeof("_ENV")},
1248    { "_REQUEST", sizeof("_REQUEST")},
1249#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1250    { "GLOBALS",  sizeof("GLOBALS")},
1251#endif
1252};
1253
1254static int zend_accel_get_auto_globals(TSRMLS_D)
1255{
1256    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1257    int n = 1;
1258    int mask = 0;
1259
1260    for (i = 0; i < ag_size ; i++) {
1261        if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_info[i].name, jit_auto_globals_info[i].len)) {
1262            mask |= n;
1263        }
1264        n += n;
1265    }
1266    return mask;
1267}
1268
1269#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1270static int zend_accel_get_auto_globals_no_jit(TSRMLS_D)
1271{
1272    if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_info[3].name, jit_auto_globals_info[3].len)) {
1273        return 8;
1274    }
1275    return 0;
1276}
1277#endif
1278
1279static void zend_accel_set_auto_globals(int mask TSRMLS_DC)
1280{
1281    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1282    int n = 1;
1283
1284    for (i = 0; i < ag_size ; i++) {
1285        if (mask & n) {
1286            zend_is_auto_global(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len - 1 TSRMLS_CC);
1287        }
1288        n += n;
1289    }
1290}
1291
1292static 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 TSRMLS_DC)
1293{
1294    zend_persistent_script *new_persistent_script;
1295    zend_op_array *orig_active_op_array;
1296    HashTable *orig_function_table, *orig_class_table;
1297    zval *orig_user_error_handler;
1298    zend_op_array *op_array;
1299    int do_bailout = 0;
1300    accel_time_t timestamp = 0;
1301#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1302    zend_uint orig_compiler_options = 0;
1303#endif
1304
1305    /* Try to open file */
1306    if (file_handle->type == ZEND_HANDLE_FILENAME) {
1307        if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == SUCCESS) {
1308            /* key may be changed by zend_stream_open_function() */
1309            if (key == ZCG(key)) {
1310                key_length = ZCG(key_len);
1311            }
1312        } else {
1313            *op_array_p = NULL;
1314            if (type == ZEND_REQUIRE) {
1315#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1316                zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1317#else
1318                zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
1319#endif
1320                zend_bailout();
1321            } else {
1322#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1323                zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1324#else
1325                zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
1326#endif
1327            }
1328            return NULL;
1329        }
1330    }
1331
1332    /* check blacklist right after ensuring that file was opened */
1333    if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, file_handle->opened_path)) {
1334        ZCSG(blacklist_misses)++;
1335        *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1336        return NULL;
1337    }
1338
1339#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1340    if (file_handle->type == ZEND_HANDLE_STREAM &&
1341        (!strstr(file_handle->filename, ".phar") ||
1342         strstr(file_handle->filename, "://"))) {
1343        char *buf;
1344        size_t size;
1345
1346        /* Stream callbacks needs to be called in context of original
1347         * function and class tables (see: https://bugs.php.net/bug.php?id=64353)
1348         */
1349        if (zend_stream_fixup(file_handle, &buf, &size TSRMLS_CC) == FAILURE) {
1350            *op_array_p = NULL;
1351            return NULL;
1352        }
1353    }
1354#endif
1355
1356    if (ZCG(accel_directives).validate_timestamps ||
1357        ZCG(accel_directives).file_update_protection ||
1358        ZCG(accel_directives).max_file_size > 0) {
1359        size_t size = 0;
1360
1361        /* Obtain the file timestamps, *before* actually compiling them,
1362         * otherwise we have a race-condition.
1363         */
1364        timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL TSRMLS_CC);
1365
1366        /* If we can't obtain a timestamp (that means file is possibly socket)
1367         *  we won't cache it
1368         */
1369        if (timestamp == 0) {
1370            *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1371            return NULL;
1372        }
1373
1374        /* check if file is too new (may be it's not written completely yet) */
1375        if (ZCG(accel_directives).file_update_protection &&
1376            (ZCG(request_time) - ZCG(accel_directives).file_update_protection < timestamp)) {
1377            *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1378            return NULL;
1379        }
1380
1381        if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1382            ZCSG(blacklist_misses)++;
1383            *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1384            return NULL;
1385        }
1386    }
1387
1388    new_persistent_script = create_persistent_script();
1389
1390    /* Save the original values for the op_array, function table and class table */
1391    orig_active_op_array = CG(active_op_array);
1392    orig_function_table = CG(function_table);
1393    orig_class_table = CG(class_table);
1394    orig_user_error_handler = EG(user_error_handler);
1395
1396    /* Override them with ours */
1397    CG(function_table) = &ZCG(function_table);
1398    EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
1399    EG(user_error_handler) = NULL;
1400
1401    zend_try {
1402#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1403        orig_compiler_options = CG(compiler_options);
1404        CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1405        CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1406        CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1407        CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1408#endif
1409        op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1410#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1411        CG(compiler_options) = orig_compiler_options;
1412#endif
1413    } zend_catch {
1414        op_array = NULL;
1415        do_bailout = 1;
1416#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1417        CG(compiler_options) = orig_compiler_options;
1418#endif
1419    } zend_end_try();
1420
1421    /* Restore originals */
1422    CG(active_op_array) = orig_active_op_array;
1423    CG(function_table) = orig_function_table;
1424    EG(class_table) = CG(class_table) = orig_class_table;
1425    EG(user_error_handler) = orig_user_error_handler;
1426
1427    if (!op_array) {
1428        /* compilation failed */
1429        free_persistent_script(new_persistent_script, 1);
1430        zend_accel_free_user_functions(&ZCG(function_table) TSRMLS_CC);
1431        if (do_bailout) {
1432            zend_bailout();
1433        }
1434        return NULL;
1435    }
1436
1437    /* Build the persistent_script structure.
1438       Here we aren't sure we would store it, but we will need it
1439       further anyway.
1440    */
1441    zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table TSRMLS_CC);
1442    new_persistent_script->main_op_array = *op_array;
1443
1444    efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1445
1446    /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1447       will have to ping the used auto global variables before execution */
1448#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1449    if (PG(auto_globals_jit)) {
1450        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals(TSRMLS_C);
1451    } else {
1452        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit(TSRMLS_C);
1453    }
1454#else
1455    if ((PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays))) {
1456        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals(TSRMLS_C);
1457    }
1458#endif
1459
1460    if (ZCG(accel_directives).validate_timestamps) {
1461        /* Obtain the file timestamps, *before* actually compiling them,
1462         * otherwise we have a race-condition.
1463         */
1464        new_persistent_script->timestamp = timestamp;
1465        new_persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at);
1466    }
1467
1468    if (file_handle->opened_path) {
1469        new_persistent_script->full_path_len = strlen(file_handle->opened_path);
1470        new_persistent_script->full_path = estrndup(file_handle->opened_path, new_persistent_script->full_path_len);
1471    } else {
1472        new_persistent_script->full_path_len = strlen(file_handle->filename);
1473        new_persistent_script->full_path = estrndup(file_handle->filename, new_persistent_script->full_path_len);
1474    }
1475    new_persistent_script->hash_value = zend_hash_func(new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
1476
1477    /* Now persistent_script structure is ready in process memory */
1478    return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory TSRMLS_CC);
1479}
1480
1481/* zend_compile() replacement */
1482zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC)
1483{
1484    zend_persistent_script *persistent_script = NULL;
1485    char *key = NULL;
1486    int key_length;
1487    int from_shared_memory; /* if the script we've got is stored in SHM */
1488
1489    if (!file_handle->filename ||
1490        !ZCG(enabled) || !accel_startup_ok ||
1491        (!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
1492        CG(interactive) ||
1493        (ZCSG(restart_in_progress) && accel_restart_is_active(TSRMLS_C)) ||
1494        (is_stream_path(file_handle->filename) &&
1495         !is_cacheable_stream_path(file_handle->filename))) {
1496        /* The Accelerator is disabled, act as if without the Accelerator */
1497        return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1498    }
1499
1500    /* Make sure we only increase the currently running processes semaphore
1501     * once each execution (this function can be called more than once on
1502     * each execution)
1503     */
1504    if (!ZCG(counted)) {
1505        ZCG(counted) = 1;
1506        accel_activate_add(TSRMLS_C);
1507    }
1508
1509    /* In case this callback is called from include_once, require_once or it's
1510     * a main FastCGI request, the key must be already calculated, and cached
1511     * persistent script already found */
1512    if ((EG(opline_ptr) == NULL &&
1513         ZCG(cache_opline) == NULL &&
1514         file_handle->filename == SG(request_info).path_translated &&
1515         ZCG(cache_persistent_script)) ||
1516        (EG(opline_ptr) && *EG(opline_ptr) &&
1517         *EG(opline_ptr) == ZCG(cache_opline) &&
1518         (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL &&
1519#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1520         ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE ||
1521          (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) {
1522#else
1523         ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
1524          (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) {
1525#endif
1526        if (!ZCG(key_len)) {
1527            return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1528        }
1529        /* persistent script was already found by overridden open() or
1530         * resolve_path() callbacks */
1531        persistent_script = ZCG(cache_persistent_script);
1532        key = ZCG(key);
1533        key_length = ZCG(key_len);
1534    } else {
1535        /* try to find cached script by key */
1536        if ((key = accel_make_persistent_key(file_handle, &key_length TSRMLS_CC)) == NULL) {
1537            return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1538        }
1539        persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1);
1540        if (!persistent_script) {
1541            /* try to find cached script by full real path */
1542            zend_accel_hash_entry *bucket;
1543
1544            /* open file to resolve the path */
1545            if (file_handle->type == ZEND_HANDLE_FILENAME &&
1546#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
1547                accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) {
1548#else
1549                zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) {
1550#endif
1551                if (type == ZEND_REQUIRE) {
1552#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1553                    zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1554#else
1555                    zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC);
1556#endif
1557                    zend_bailout();
1558                } else {
1559#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1560                    zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1561#else
1562                    zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC);
1563#endif
1564                }
1565                return NULL;
1566            }
1567
1568            if (file_handle->opened_path &&
1569                (bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path, strlen(file_handle->opened_path) + 1)) != NULL) {
1570
1571                persistent_script = (zend_persistent_script *)bucket->data;
1572                if (!ZCG(accel_directives).revalidate_path &&
1573                    !persistent_script->corrupted) {
1574                    SHM_UNPROTECT();
1575                    zend_shared_alloc_lock(TSRMLS_C);
1576                    zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
1577                    zend_shared_alloc_unlock(TSRMLS_C);
1578                    SHM_PROTECT();
1579                }
1580            }
1581        }
1582    }
1583
1584    /* clear cache */
1585    ZCG(cache_opline) = NULL;
1586    ZCG(cache_persistent_script) = NULL;
1587
1588    if (persistent_script && persistent_script->corrupted) {
1589        persistent_script = NULL;
1590    }
1591
1592    SHM_UNPROTECT();
1593
1594    /* If script is found then validate_timestamps if option is enabled */
1595    if (persistent_script && ZCG(accel_directives).validate_timestamps) {
1596        if (validate_timestamp_and_record(persistent_script, file_handle TSRMLS_CC) == FAILURE) {
1597            zend_shared_alloc_lock(TSRMLS_C);
1598            if (!persistent_script->corrupted) {
1599                persistent_script->corrupted = 1;
1600                persistent_script->timestamp = 0;
1601                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1602                if (ZSMMG(memory_exhausted)) {
1603                    zend_accel_restart_reason reason =
1604                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1605                    zend_accel_schedule_restart_if_necessary(reason TSRMLS_CC);
1606                }
1607            }
1608            zend_shared_alloc_unlock(TSRMLS_C);
1609            persistent_script = NULL;
1610        }
1611    }
1612
1613    /* if turned on - check the compiled script ADLER32 checksum */
1614    if (persistent_script && ZCG(accel_directives).consistency_checks
1615        && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
1616
1617        unsigned int checksum = zend_accel_script_checksum(persistent_script);
1618        if (checksum != persistent_script->dynamic_members.checksum ) {
1619            /* The checksum is wrong */
1620            zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s':  expected=0x%0.8X, found=0x%0.8X",
1621                             persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum);
1622            zend_shared_alloc_lock(TSRMLS_C);
1623            if (!persistent_script->corrupted) {
1624                persistent_script->corrupted = 1;
1625                persistent_script->timestamp = 0;
1626                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1627                if (ZSMMG(memory_exhausted)) {
1628                    zend_accel_restart_reason reason =
1629                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1630                    zend_accel_schedule_restart_if_necessary(reason TSRMLS_CC);
1631                }
1632            }
1633            zend_shared_alloc_unlock(TSRMLS_C);
1634            persistent_script = NULL;
1635        }
1636    }
1637
1638    /* If script was not found or invalidated by validate_timestamps */
1639    if (!persistent_script) {
1640        zend_op_array *op_array;
1641
1642        /* Cache miss.. */
1643        ZCSG(misses)++;
1644
1645        /* No memory left. Behave like without the Accelerator */
1646        if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
1647            SHM_PROTECT();
1648            return accelerator_orig_compile_file(file_handle, type TSRMLS_CC);
1649        }
1650
1651        /* Try and cache the script and assume that it is returned from_shared_memory.
1652         * If it isn't compile_and_cache_file() changes the flag to 0
1653         */
1654        from_shared_memory = 0;
1655        persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory TSRMLS_CC);
1656
1657        /* Caching is disabled, returning op_array;
1658         * or something went wrong during compilation, returning NULL
1659         */
1660        if (!persistent_script) {
1661            SHM_PROTECT();
1662            return op_array;
1663        }
1664    } else {
1665
1666#if !ZEND_WIN32
1667        ZCSG(hits)++; /* TBFixed: may lose one hit */
1668        persistent_script->dynamic_members.hits++; /* see above */
1669#else
1670        InterlockedIncrement(&ZCSG(hits));
1671        InterlockedIncrement(&persistent_script->dynamic_members.hits);
1672#endif
1673
1674        /* see bug #15471 (old BTS) */
1675        if (persistent_script->full_path) {
1676            if (!EG(opline_ptr) || !*EG(opline_ptr) ||
1677                (*EG(opline_ptr))->opcode != ZEND_INCLUDE_OR_EVAL ||
1678#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1679                ((*EG(opline_ptr))->extended_value != ZEND_INCLUDE_ONCE &&
1680                 (*EG(opline_ptr))->extended_value != ZEND_REQUIRE_ONCE)) {
1681#else
1682                ((*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_INCLUDE_ONCE &&
1683                 (*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_REQUIRE_ONCE)) {
1684#endif
1685                void *dummy = (void *) 1;
1686
1687                if (zend_hash_quick_add(&EG(included_files), persistent_script->full_path, persistent_script->full_path_len + 1, persistent_script->hash_value, &dummy, sizeof(void *), NULL) == SUCCESS) {
1688                    /* ext/phar has to load phar's metadata into memory */
1689                    if (strstr(persistent_script->full_path, ".phar") && !strstr(persistent_script->full_path, "://")) {
1690                        php_stream_statbuf ssb;
1691                        char *fname = emalloc(sizeof("phar://") + persistent_script->full_path_len);
1692
1693                        memcpy(fname, "phar://", sizeof("phar://") - 1);
1694                        memcpy(fname + sizeof("phar://") - 1, persistent_script->full_path, persistent_script->full_path_len + 1);
1695                        php_stream_stat_path(fname, &ssb);
1696                        efree(fname);
1697                    }
1698                }
1699            }
1700        }
1701#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1702        zend_file_handle_dtor(file_handle);
1703#else
1704        zend_file_handle_dtor(file_handle TSRMLS_CC);
1705#endif
1706        from_shared_memory = 1;
1707    }
1708
1709    persistent_script->dynamic_members.last_used = ZCG(request_time);
1710
1711    SHM_PROTECT();
1712
1713    /* Fetch jit auto globals used in the script before execution */
1714    if (persistent_script->ping_auto_globals_mask) {
1715        zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask TSRMLS_CC);
1716    }
1717
1718    return zend_accel_load_script(persistent_script, from_shared_memory TSRMLS_CC);
1719}
1720
1721#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
1722
1723/* Taken from php-5.2.5 because early versions don't have correct version */
1724static char *accel_tsrm_realpath(const char *path, int path_len TSRMLS_DC)
1725{
1726    cwd_state new_state;
1727    char *real_path;
1728    char *cwd;
1729    int cwd_len;
1730
1731    /* realpath("") returns CWD */
1732    if (!*path) {
1733        new_state.cwd = (char*)malloc(1);
1734        if (!new_state.cwd) {
1735            zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed");
1736            return NULL;
1737        }
1738        new_state.cwd[0] = '\0';
1739        new_state.cwd_length = 0;
1740        if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) {
1741            path = cwd;
1742        }
1743    } else if (!IS_ABSOLUTE_PATH(path, path_len) &&
1744        (cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) {
1745        new_state.cwd = zend_strndup(cwd, cwd_len);
1746        if (!new_state.cwd) {
1747            zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed");
1748            return NULL;
1749        }
1750        new_state.cwd_length = cwd_len;
1751    } else {
1752        new_state.cwd = (char*)malloc(1);
1753        if (!new_state.cwd) {
1754            zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed");
1755            return NULL;
1756        }
1757        new_state.cwd[0] = '\0';
1758        new_state.cwd_length = 0;
1759    }
1760
1761#ifndef CWD_REALPATH
1762# define CWD_REALPATH 2
1763#endif
1764    if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1765        free(new_state.cwd);
1766        return NULL;
1767    }
1768
1769    real_path = emalloc(new_state.cwd_length + 1);
1770    memcpy(real_path, new_state.cwd, new_state.cwd_length + 1);
1771    free(new_state.cwd);
1772    return real_path;
1773}
1774
1775static char *accel_php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC)
1776{
1777    char *resolved_path;
1778    char trypath[MAXPATHLEN];
1779    const char *ptr, *end;
1780    int len;
1781
1782    if (!filename) {
1783        return NULL;
1784    }
1785
1786    if (*filename == '.' ||
1787        IS_ABSOLUTE_PATH(filename, filename_length) ||
1788        !path ||
1789        !*path) {
1790        return accel_tsrm_realpath(filename, filename_length TSRMLS_CC);
1791    }
1792
1793    ptr = path;
1794    while (*ptr) {
1795        for (end = ptr; *end && *end != DEFAULT_DIR_SEPARATOR; end++);
1796        len = end - ptr;
1797        if (*end) end++;
1798        if (len + 1 + filename_length + 1 >= MAXPATHLEN) {
1799            ptr = end;
1800            continue;
1801        }
1802        memcpy(trypath, ptr, len);
1803        trypath[len] = '/';
1804        memcpy(trypath + len + 1, filename, filename_length + 1);
1805        ptr = end;
1806        if ((resolved_path = accel_tsrm_realpath(trypath, len + 1 + filename_length TSRMLS_CC)) != NULL) {
1807            return resolved_path;
1808        }
1809    } /* end provided path */
1810
1811    /* check in calling scripts' current working directory as a fall back case
1812     */
1813    if (EG(in_execution)) {
1814        char *exec_fname = zend_get_executed_filename(TSRMLS_C);
1815        int exec_fname_length = strlen(exec_fname);
1816
1817        while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length]));
1818        if (exec_fname && exec_fname[0] != '[' &&
1819            exec_fname_length > 0 &&
1820            exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) {
1821            memcpy(trypath, exec_fname, exec_fname_length + 1);
1822            memcpy(trypath + exec_fname_length + 1, filename, filename_length + 1);
1823            return accel_tsrm_realpath(trypath, exec_fname_length + 1 + filename_length TSRMLS_CC);
1824        }
1825    }
1826
1827    return NULL;
1828}
1829
1830/* zend_stream_open_function() replacement for PHP 5.2 */
1831static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC)
1832{
1833    if (ZCG(enabled) && accel_startup_ok &&
1834        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1835        !CG(interactive) &&
1836        !ZCSG(restart_in_progress)) {
1837
1838        if (EG(opline_ptr) && *EG(opline_ptr)) {
1839            zend_op *opline = *EG(opline_ptr);
1840
1841            if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
1842                (opline->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
1843                 opline->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE)) {
1844                /* we are in include_once */
1845                char *key = NULL;
1846                int key_length;
1847                char *resolved_path;
1848                zend_accel_hash_entry *bucket;
1849                zend_persistent_script *persistent_script;
1850                int filename_len;
1851
1852                if (opline->op1.op_type == IS_CONST) {
1853                    filename_len = Z_STRLEN(opline->op1.u.constant);
1854                } else {
1855                    filename_len = strlen(filename);
1856                }
1857                handle->filename = (char*)filename;
1858                handle->free_filename = 0;
1859                handle->opened_path = NULL;
1860
1861                /* Check if requested file already cached (by full name) */
1862                if (IS_ABSOLUTE_PATH(filename, filename_len) &&
1863                    (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len + 1)) != NULL &&
1864                    !persistent_script->corrupted) {
1865
1866                    handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
1867                    handle->type = ZEND_HANDLE_FILENAME;
1868                    memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1);
1869                    ZCG(key_len) = persistent_script->full_path_len;
1870                    ZCG(cache_opline) = opline;
1871                    ZCG(cache_persistent_script) = persistent_script;
1872                    return SUCCESS;
1873                }
1874
1875                /* Check if requested file already cached (by key) */
1876                key = accel_make_persistent_key_ex(handle, filename_len, &key_length TSRMLS_CC);
1877                if (!ZCG(accel_directives).revalidate_path &&
1878                    key &&
1879                    (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) != NULL &&
1880                    !persistent_script->corrupted) {
1881
1882                    handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
1883                    handle->type = ZEND_HANDLE_FILENAME;
1884                    ZCG(cache_opline) = opline;
1885                    ZCG(cache_persistent_script) = persistent_script;
1886                    return SUCCESS;
1887                }
1888
1889                /* find the full real path */
1890                resolved_path = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC);
1891
1892                /* Check if requested file already cached (by real name) */
1893                if (resolved_path &&
1894                    (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path) + 1)) != NULL) {
1895
1896                    persistent_script = (zend_persistent_script *)bucket->data;
1897                    if (persistent_script && !persistent_script->corrupted) {
1898                        handle->opened_path = resolved_path;
1899                        handle->type = ZEND_HANDLE_FILENAME;
1900                        if (key && !ZCG(accel_directives).revalidate_path) {
1901                            /* add another "key" for the same bucket */
1902                            SHM_UNPROTECT();
1903                            zend_shared_alloc_lock(TSRMLS_C);
1904                            zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
1905                            zend_shared_alloc_unlock(TSRMLS_C);
1906                            SHM_PROTECT();
1907                        }
1908                        ZCG(cache_opline) = opline;
1909                        ZCG(cache_persistent_script) = persistent_script;
1910                        return SUCCESS;
1911                    }
1912                }
1913                if (resolved_path) {
1914                    efree(resolved_path);
1915                }
1916            }
1917        }
1918    }
1919    ZCG(cache_opline) = NULL;
1920    ZCG(cache_persistent_script) = NULL;
1921    return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC);
1922}
1923
1924#else
1925
1926/* zend_stream_open_function() replacement for PHP 5.3 and above */
1927static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC)
1928{
1929    if (ZCG(enabled) && accel_startup_ok &&
1930        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1931        !CG(interactive) &&
1932        !ZCSG(restart_in_progress)) {
1933
1934        /* check if callback is called from include_once or it's a main request */
1935        if ((!EG(opline_ptr) &&
1936             filename == SG(request_info).path_translated) ||
1937            (EG(opline_ptr) &&
1938             *EG(opline_ptr) &&
1939             (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL &&
1940#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
1941             ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE ||
1942              (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) {
1943#else
1944              ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
1945               (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) {
1946#endif
1947
1948            /* we are in include_once or FastCGI request */
1949            zend_persistent_script *persistent_script;
1950
1951            handle->filename = (char*)filename;
1952            handle->free_filename = 0;
1953
1954            /* check if cached script was already found by resolve_path() */
1955            if ((EG(opline_ptr) == NULL &&
1956                 ZCG(cache_opline) == NULL &&
1957                 ZCG(cache_persistent_script) != NULL) ||
1958                (EG(opline_ptr) &&
1959                 (ZCG(cache_opline) == *EG(opline_ptr)))) {
1960                persistent_script = ZCG(cache_persistent_script);
1961                handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
1962                handle->type = ZEND_HANDLE_FILENAME;
1963                return SUCCESS;
1964#if 0
1965            } else {
1966                /* FIXME: It looks like this part is not needed any more */
1967                int filename_len = strlen(filename);
1968
1969                if ((IS_ABSOLUTE_PATH(filename, filename_len) ||
1970                     is_stream_path(filename)) &&
1971                    (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len + 1)) != NULL &&
1972                    !persistent_script->corrupted) {
1973
1974                    handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len);
1975                    handle->type = ZEND_HANDLE_FILENAME;
1976                    memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1);
1977                    ZCG(key_len) = persistent_script->full_path_len;
1978                    ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL;
1979                    ZCG(cache_persistent_script) = EG(opline_ptr) ? persistent_script : NULL;
1980                    return SUCCESS;
1981                }
1982#endif
1983            }
1984        }
1985    }
1986    ZCG(cache_opline) = NULL;
1987    ZCG(cache_persistent_script) = NULL;
1988    return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC);
1989}
1990
1991/* zend_resolve_path() replacement for PHP 5.3 and above */
1992static char* persistent_zend_resolve_path(const char *filename, int filename_len TSRMLS_DC)
1993{
1994    if (ZCG(enabled) && accel_startup_ok &&
1995        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1996        !CG(interactive) &&
1997        !ZCSG(restart_in_progress)) {
1998
1999        /* check if callback is called from include_once or it's a main request */
2000        if ((!EG(opline_ptr) &&
2001             filename == SG(request_info).path_translated) ||
2002            (EG(opline_ptr) &&
2003             *EG(opline_ptr) &&
2004             (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL &&
2005#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2006             ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE ||
2007              (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) {
2008#else
2009              ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE ||
2010               (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) {
2011#endif
2012
2013            /* we are in include_once or FastCGI request */
2014            zend_file_handle handle;
2015            char *key = NULL;
2016            int key_length;
2017            char *resolved_path;
2018            zend_accel_hash_entry *bucket;
2019            zend_persistent_script *persistent_script;
2020
2021            /* Check if requested file already cached (by full name) */
2022            if ((IS_ABSOLUTE_PATH(filename, filename_len) ||
2023                 is_stream_path(filename)) &&
2024                (bucket = zend_accel_hash_find_entry(&ZCSG(hash), (char*)filename, filename_len + 1)) != NULL) {
2025                persistent_script = (zend_persistent_script *)bucket->data;
2026                if (persistent_script && !persistent_script->corrupted) {
2027                    memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1);
2028                    ZCG(key_len) = persistent_script->full_path_len;
2029                    ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL;
2030                    ZCG(cache_persistent_script) = persistent_script;
2031                    return estrndup(persistent_script->full_path, persistent_script->full_path_len);
2032                }
2033            }
2034
2035            /* Check if requested file already cached (by key) */
2036            handle.filename = (char*)filename;
2037            handle.free_filename = 0;
2038            handle.opened_path = NULL;
2039            key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC);
2040            if (!ZCG(accel_directives).revalidate_path &&
2041                key &&
2042                (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) != NULL &&
2043                !persistent_script->corrupted) {
2044
2045                /* we have persistent script */
2046                ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL;
2047                ZCG(cache_persistent_script) = persistent_script;
2048                return estrndup(persistent_script->full_path, persistent_script->full_path_len);
2049            }
2050
2051            /* find the full real path */
2052            resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
2053
2054            /* Check if requested file already cached (by real path) */
2055            if (resolved_path &&
2056                (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path) + 1)) != NULL) {
2057                persistent_script = (zend_persistent_script *)bucket->data;
2058
2059                if (persistent_script && !persistent_script->corrupted) {
2060                    if (key && !ZCG(accel_directives).revalidate_path) {
2061                        /* add another "key" for the same bucket */
2062                        SHM_UNPROTECT();
2063                        zend_shared_alloc_lock(TSRMLS_C);
2064                        zend_accel_add_key(key, key_length, bucket TSRMLS_CC);
2065                        zend_shared_alloc_unlock(TSRMLS_C);
2066                        SHM_PROTECT();
2067                    }
2068                    ZCG(cache_opline) = (EG(opline_ptr) && key) ? *EG(opline_ptr): NULL;
2069                    ZCG(cache_persistent_script) = key ? persistent_script : NULL;
2070                    return resolved_path;
2071                }
2072            }
2073            ZCG(cache_opline) = NULL;
2074            ZCG(cache_persistent_script) = NULL;
2075            return resolved_path;
2076        }
2077    }
2078    ZCG(cache_opline) = NULL;
2079    ZCG(cache_persistent_script) = NULL;
2080    return accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC);
2081}
2082
2083#endif
2084
2085static void zend_reset_cache_vars(TSRMLS_D)
2086{
2087    ZSMMG(memory_exhausted) = 0;
2088    ZCSG(hits) = 0;
2089    ZCSG(misses) = 0;
2090    ZCSG(blacklist_misses) = 0;
2091    ZSMMG(wasted_shared_memory) = 0;
2092    ZCSG(restart_pending) = 0;
2093    ZCSG(force_restart_time) = 0;
2094}
2095
2096static void accel_activate(void)
2097{
2098    TSRMLS_FETCH();
2099
2100    if (!ZCG(enabled) || !accel_startup_ok) {
2101        return;
2102    }
2103
2104    SHM_UNPROTECT();
2105    /* PHP-5.4 and above return "double", but we use 1 sec precision */
2106    ZCG(request_time) = (time_t)sapi_get_request_time(TSRMLS_C);
2107    ZCG(cache_opline) = NULL;
2108    ZCG(cache_persistent_script) = NULL;
2109    ZCG(include_path_check) = !ZCG(include_path_key);
2110
2111    if (ZCG(counted)) {
2112#ifdef ZTS
2113        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
2114#else
2115        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
2116#endif
2117        accel_unlock_all(TSRMLS_C);
2118        ZCG(counted) = 0;
2119    }
2120
2121    if (ZCSG(restart_pending)) {
2122        zend_shared_alloc_lock(TSRMLS_C);
2123        if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
2124            if (accel_is_inactive(TSRMLS_C) == SUCCESS) {
2125                zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
2126                ZCSG(restart_pending) = 0;
2127                switch ZCSG(restart_reason) {
2128                    case ACCEL_RESTART_OOM:
2129                        ZCSG(oom_restarts)++;
2130                        break;
2131                    case ACCEL_RESTART_HASH:
2132                        ZCSG(hash_restarts)++;
2133                        break;
2134                    case ACCEL_RESTART_USER:
2135                        ZCSG(manual_restarts)++;
2136                        break;
2137                }
2138                accel_restart_enter(TSRMLS_C);
2139
2140                zend_reset_cache_vars(TSRMLS_C);
2141                zend_accel_hash_clean(&ZCSG(hash));
2142
2143                /* include_paths keeps only the first path */
2144                if (ZCSG(include_paths).num_entries > 1) {
2145                    ZCSG(include_paths).num_entries = 1;
2146                    ZCSG(include_paths).num_direct_entries = 1;
2147                    memset(ZCSG(include_paths).hash_table, 0, sizeof(zend_accel_hash_entry*) * ZCSG(include_paths).max_num_entries);
2148                    ZCSG(include_paths).hash_table[zend_inline_hash_func(ZCSG(include_paths).hash_entries[0].key, ZCSG(include_paths).hash_entries[0].key_length) % ZCSG(include_paths).max_num_entries] = &ZCSG(include_paths).hash_entries[0];
2149                }
2150
2151#if (ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO) && !defined(ZTS)
2152                if (ZCG(accel_directives).interned_strings_buffer) {
2153                    accel_interned_strings_restore_state(TSRMLS_C);
2154                }
2155#endif
2156
2157                zend_shared_alloc_restore_state();
2158                ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
2159                ZCSG(last_restart_time) = ZCG(request_time);
2160                accel_restart_leave(TSRMLS_C);
2161            }
2162        }
2163        zend_shared_alloc_unlock(TSRMLS_C);
2164    }
2165
2166    /* check if ZCG(function_table) wasn't somehow polluted on the way */
2167    if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
2168        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)));
2169    }
2170
2171    if (ZCG(accel_directives).validate_timestamps) {
2172        time_t now = ZCG(request_time);
2173        if (now > ZCSG(revalidate_at) + (time_t)ZCG(accel_directives).revalidate_freq) {
2174            ZCSG(revalidate_at) = now;
2175        }
2176    }
2177
2178    ZCG(cwd) = NULL;
2179
2180    SHM_PROTECT();
2181}
2182
2183#if !ZEND_DEBUG
2184
2185/* Fast Request Shutdown
2186 * =====================
2187 * Zend Memory Manager frees memory by its own. We don't have to free each
2188 * allocated block separately, but we like to call all the destructors and
2189 * callbacks in exactly the same order.
2190 */
2191
2192static void accel_fast_hash_destroy(HashTable *ht)
2193{
2194    Bucket *p = ht->pListHead;
2195
2196    while (p != NULL) {
2197        ht->pDestructor(p->pData);
2198        p = p->pListNext;
2199    }
2200}
2201
2202static void accel_fast_zval_ptr_dtor(zval **zval_ptr)
2203{
2204    zval *zvalue = *zval_ptr;
2205
2206    if (Z_DELREF_P(zvalue) == 0) {
2207#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
2208        switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) {
2209#else
2210        switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) {
2211#endif
2212#if ZEND_EXTENSION_API_NO <= PHP_5_5_API_NO
2213            case IS_CONSTANT_ARRAY:
2214#endif
2215            case IS_ARRAY: {
2216                    TSRMLS_FETCH();
2217
2218#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
2219                    GC_REMOVE_ZVAL_FROM_BUFFER(zvalue);
2220#endif
2221                    if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) {
2222                        /* break possible cycles */
2223                        Z_TYPE_P(zvalue) = IS_NULL;
2224                        zvalue->value.ht->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
2225                        accel_fast_hash_destroy(zvalue->value.ht);
2226                    }
2227                }
2228                break;
2229            case IS_OBJECT:
2230                {
2231                    TSRMLS_FETCH();
2232
2233#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
2234                    GC_REMOVE_ZVAL_FROM_BUFFER(zvalue);
2235#endif
2236                    Z_OBJ_HT_P(zvalue)->del_ref(zvalue TSRMLS_CC);
2237                }
2238                break;
2239            case IS_RESOURCE:
2240                {
2241                    TSRMLS_FETCH();
2242
2243                    /* destroy resource */
2244                    zend_list_delete(zvalue->value.lval);
2245                }
2246                break;
2247            case IS_LONG:
2248            case IS_DOUBLE:
2249            case IS_BOOL:
2250            case IS_NULL:
2251            case IS_STRING:
2252            case IS_CONSTANT:
2253            default:
2254                return;
2255                break;
2256        }
2257    }
2258}
2259
2260static int accel_clean_non_persistent_function(zend_function *function TSRMLS_DC)
2261{
2262    if (function->type == ZEND_INTERNAL_FUNCTION) {
2263        return ZEND_HASH_APPLY_STOP;
2264    } else {
2265        if (function->op_array.static_variables) {
2266            function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
2267            accel_fast_hash_destroy(function->op_array.static_variables);
2268            function->op_array.static_variables = NULL;
2269        }
2270        return (--(*function->op_array.refcount) <= 0) ?
2271            ZEND_HASH_APPLY_REMOVE :
2272            ZEND_HASH_APPLY_KEEP;
2273    }
2274}
2275
2276static int accel_cleanup_function_data(zend_function *function TSRMLS_DC)
2277{
2278    if (function->type == ZEND_USER_FUNCTION) {
2279        if (function->op_array.static_variables) {
2280            function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
2281            accel_fast_hash_destroy(function->op_array.static_variables);
2282            function->op_array.static_variables = NULL;
2283        }
2284    }
2285    return 0;
2286}
2287
2288static int accel_clean_non_persistent_class(zend_class_entry **pce TSRMLS_DC)
2289{
2290    zend_class_entry *ce = *pce;
2291
2292    if (ce->type == ZEND_INTERNAL_CLASS) {
2293        return ZEND_HASH_APPLY_STOP;
2294    } else {
2295#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2296        if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
2297            zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC);
2298        }
2299        if (ce->static_members_table) {
2300            int i;
2301
2302            for (i = 0; i < ce->default_static_members_count; i++) {
2303                if (ce->static_members_table[i]) {
2304                    accel_fast_zval_ptr_dtor(&ce->static_members_table[i]);
2305                    ce->static_members_table[i] = NULL;
2306                }
2307            }
2308            ce->static_members_table = NULL;
2309        }
2310#else
2311        zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC);
2312        if (ce->static_members) {
2313            ce->static_members->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
2314            accel_fast_hash_destroy(ce->static_members);
2315            ce->static_members = NULL;
2316        }
2317#endif
2318        return ZEND_HASH_APPLY_REMOVE;
2319    }
2320}
2321
2322static int accel_clean_non_persistent_constant(zend_constant *c TSRMLS_DC)
2323{
2324    if (c->flags & CONST_PERSISTENT) {
2325        return ZEND_HASH_APPLY_STOP;
2326    } else {
2327        interned_free(c->name);
2328        return ZEND_HASH_APPLY_REMOVE;
2329    }
2330}
2331
2332static void zend_accel_fast_shutdown(TSRMLS_D)
2333{
2334    if (EG(full_tables_cleanup)) {
2335        EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
2336    } else {
2337        dtor_func_t old_destructor;
2338
2339        if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) {
2340            /* We don't have to destroy all zvals if they cannot call any destructors */
2341
2342            old_destructor = EG(symbol_table).pDestructor;
2343            EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor;
2344            zend_try {
2345                zend_hash_graceful_reverse_destroy(&EG(symbol_table));
2346            } zend_end_try();
2347            EG(symbol_table).pDestructor = old_destructor;
2348        }
2349        zend_hash_init(&EG(symbol_table), 0, NULL, NULL, 0);
2350        old_destructor = EG(function_table)->pDestructor;
2351        EG(function_table)->pDestructor = NULL;
2352        zend_hash_reverse_apply(EG(function_table), (apply_func_t) accel_clean_non_persistent_function TSRMLS_CC);
2353        EG(function_table)->pDestructor = old_destructor;
2354        old_destructor = EG(class_table)->pDestructor;
2355        EG(class_table)->pDestructor = NULL;
2356        zend_hash_reverse_apply(EG(class_table), (apply_func_t) accel_clean_non_persistent_class TSRMLS_CC);
2357        EG(class_table)->pDestructor = old_destructor;
2358        old_destructor = EG(zend_constants)->pDestructor;
2359        EG(zend_constants)->pDestructor = NULL;
2360        zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) accel_clean_non_persistent_constant TSRMLS_CC);
2361        EG(zend_constants)->pDestructor = old_destructor;
2362    }
2363    CG(unclean_shutdown) = 1;
2364}
2365#endif
2366
2367static void accel_deactivate(void)
2368{
2369    /* ensure that we restore function_table and class_table
2370     * In general, they're restored by persistent_compile_file(), but in case
2371     * the script is aborted abnormally, they may become messed up.
2372     */
2373    TSRMLS_FETCH();
2374
2375    if (!ZCG(enabled) || !accel_startup_ok) {
2376        return;
2377    }
2378
2379    zend_shared_alloc_safe_unlock(TSRMLS_C); /* be sure we didn't leave cache locked */
2380    accel_unlock_all(TSRMLS_C);
2381    ZCG(counted) = 0;
2382
2383#if !ZEND_DEBUG
2384    if (ZCG(accel_directives).fast_shutdown) {
2385        zend_accel_fast_shutdown(TSRMLS_C);
2386    }
2387#endif
2388
2389    if (ZCG(cwd)) {
2390        efree(ZCG(cwd));
2391        ZCG(cwd) = NULL;
2392    }
2393
2394}
2395
2396static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
2397{
2398    (void)element2; /* keep the compiler happy */
2399
2400    if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
2401        element1->startup = NULL;
2402#if 0
2403        /* We have to call shutdown callback it to free TS resources */
2404        element1->shutdown = NULL;
2405#endif
2406        element1->activate = NULL;
2407        element1->deactivate = NULL;
2408        element1->op_array_handler = NULL;
2409
2410#ifdef __DEBUG_MESSAGES__
2411        fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
2412        fflush(stderr);
2413#endif
2414    }
2415
2416    return 0;
2417}
2418
2419static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *) TSRMLS_DC)
2420{
2421    accel_startup_ok = 0;
2422    zps_failure_reason = reason;
2423    zps_api_failure_reason = api_reason?api_reason:reason;
2424    zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
2425}
2426
2427static inline int accel_find_sapi(TSRMLS_D)
2428{
2429    static const char *supported_sapis[] = {
2430        "apache",
2431        "fastcgi",
2432        "cli-server",
2433        "cgi-fcgi",
2434        "fpm-fcgi",
2435        "isapi",
2436        "apache2filter",
2437        "apache2handler",
2438        "litespeed",
2439        NULL
2440    };
2441    const char **sapi_name;
2442
2443    if (sapi_module.name) {
2444        for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
2445            if (strcmp(sapi_module.name, *sapi_name) == 0) {
2446                return SUCCESS;
2447            }
2448        }
2449        if (ZCG(accel_directives).enable_cli &&
2450            strcmp(sapi_module.name, "cli") == 0) {
2451            return SUCCESS;
2452        }
2453    }
2454
2455    return FAILURE;
2456}
2457
2458static int zend_accel_init_shm(TSRMLS_D)
2459{
2460    zend_shared_alloc_lock(TSRMLS_C);
2461
2462    accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals));
2463    if (!accel_shared_globals) {
2464        zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2465        return FAILURE;
2466    }
2467    ZSMMG(app_shared_globals) = accel_shared_globals;
2468
2469    zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
2470    zend_accel_hash_init(&ZCSG(include_paths), 32);
2471
2472#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2473
2474    ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL;
2475# ifndef ZTS
2476    zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / (sizeof(Bucket) + sizeof(Bucket*) + 8 /* average string length */), NULL, NULL, 1);
2477    if (ZCG(accel_directives).interned_strings_buffer) {
2478        ZCSG(interned_strings).nTableMask = ZCSG(interned_strings).nTableSize - 1;
2479        ZCSG(interned_strings).arBuckets = zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *));
2480        ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2481        if (!ZCSG(interned_strings).arBuckets || !ZCSG(interned_strings_start)) {
2482            zend_accel_error(ACCEL_LOG_FATAL, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings");
2483            return FAILURE;
2484        }
2485        ZCSG(interned_strings_end)   = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2486        ZCSG(interned_strings_top)   = ZCSG(interned_strings_start);
2487
2488        orig_interned_strings_start = CG(interned_strings_start);
2489        orig_interned_strings_end = CG(interned_strings_end);
2490        CG(interned_strings_start) = ZCSG(interned_strings_start);
2491        CG(interned_strings_end) = ZCSG(interned_strings_end);
2492    }
2493# endif
2494
2495    orig_new_interned_string = zend_new_interned_string;
2496    orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2497    orig_interned_strings_restore = zend_interned_strings_restore;
2498    zend_new_interned_string = accel_new_interned_string_for_php;
2499    zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2500    zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2501
2502# ifndef ZTS
2503    if (ZCG(accel_directives).interned_strings_buffer) {
2504        accel_use_shm_interned_strings(TSRMLS_C);
2505        accel_interned_strings_save_state(TSRMLS_C);
2506    }
2507# endif
2508
2509#endif
2510
2511    zend_reset_cache_vars(TSRMLS_C);
2512
2513    ZCSG(oom_restarts) = 0;
2514    ZCSG(hash_restarts) = 0;
2515    ZCSG(manual_restarts) = 0;
2516
2517    ZCSG(accelerator_enabled) = 1;
2518    ZCSG(start_time) = zend_accel_get_time();
2519    ZCSG(last_restart_time) = 0;
2520    ZCSG(restart_in_progress) = 0;
2521
2522    zend_shared_alloc_unlock(TSRMLS_C);
2523
2524    return SUCCESS;
2525}
2526
2527static void accel_globals_ctor(zend_accel_globals *accel_globals TSRMLS_DC)
2528{
2529    memset(accel_globals, 0, sizeof(zend_accel_globals));
2530    zend_hash_init(&accel_globals->function_table, zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1);
2531    zend_accel_copy_internal_functions(TSRMLS_C);
2532}
2533
2534static void accel_globals_dtor(zend_accel_globals *accel_globals TSRMLS_DC)
2535{
2536    accel_globals->function_table.pDestructor = NULL;
2537    zend_hash_destroy(&accel_globals->function_table);
2538}
2539
2540static int accel_startup(zend_extension *extension)
2541{
2542    zend_function *func;
2543    zend_ini_entry *ini_entry;
2544    TSRMLS_FETCH();
2545
2546#ifdef ZTS
2547    accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor);
2548#else
2549    accel_globals_ctor(&accel_globals);
2550#endif
2551
2552#ifdef ZEND_WIN32
2553    _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
2554#endif
2555
2556#if ZEND_EXTENSION_API_NO > PHP_5_6_X_API_NO
2557    if (start_accel_module(TSRMLS_C) == FAILURE) {
2558#else
2559    if (start_accel_module() == FAILURE) {
2560#endif
2561        accel_startup_ok = 0;
2562        zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
2563        return FAILURE;
2564    }
2565
2566    /* no supported SAPI found - disable acceleration and stop initialization */
2567    if (accel_find_sapi(TSRMLS_C) == FAILURE) {
2568        accel_startup_ok = 0;
2569        if (!ZCG(accel_directives).enable_cli &&
2570            strcmp(sapi_module.name, "cli") == 0) {
2571            zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb TSRMLS_CC);
2572        } else {
2573            zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI, FPM, FastCGI and LiteSpeed SAPIs", NULL, accelerator_remove_cb TSRMLS_CC);
2574        }
2575        return SUCCESS;
2576    }
2577
2578    if (ZCG(enabled) == 0) {
2579        return SUCCESS ;
2580    }
2581/********************************************/
2582/* End of non-SHM dependent initializations */
2583/********************************************/
2584    switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
2585        case ALLOC_SUCCESS:
2586            if (zend_accel_init_shm(TSRMLS_C) == FAILURE) {
2587                accel_startup_ok = 0;
2588                return FAILURE;
2589            }
2590            break;
2591        case ALLOC_FAILURE:
2592            accel_startup_ok = 0;
2593            zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
2594            return SUCCESS;
2595        case SUCCESSFULLY_REATTACHED:
2596            accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
2597#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2598            zend_shared_alloc_lock(TSRMLS_C);
2599            orig_interned_strings_start = CG(interned_strings_start);
2600            orig_interned_strings_end = CG(interned_strings_end);
2601            orig_new_interned_string = zend_new_interned_string;
2602            orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2603            orig_interned_strings_restore = zend_interned_strings_restore;
2604
2605            CG(interned_strings_start) = ZCSG(interned_strings_start);
2606            CG(interned_strings_end) = ZCSG(interned_strings_end);
2607            zend_new_interned_string = accel_new_interned_string_for_php;
2608            zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2609            zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2610
2611# ifndef ZTS
2612            accel_use_shm_interned_strings(TSRMLS_C);
2613# endif
2614
2615            zend_shared_alloc_unlock(TSRMLS_C);
2616#endif
2617
2618            break;
2619        case FAILED_REATTACHED:
2620            accel_startup_ok = 0;
2621            zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
2622            return SUCCESS;
2623            break;
2624    }
2625
2626    /* from this point further, shared memory is supposed to be OK */
2627
2628    /* Override compiler */
2629    accelerator_orig_compile_file = zend_compile_file;
2630    zend_compile_file = persistent_compile_file;
2631
2632    /* Override stream opener function (to eliminate open() call caused by
2633     * include/require statements ) */
2634    accelerator_orig_zend_stream_open_function = zend_stream_open_function;
2635    zend_stream_open_function = persistent_stream_open_function;
2636
2637#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
2638    /* Override path resolver function (to eliminate stat() calls caused by
2639     * include_once/require_once statements */
2640    accelerator_orig_zend_resolve_path = zend_resolve_path;
2641    zend_resolve_path = persistent_zend_resolve_path;
2642#endif
2643
2644    if (ZCG(accel_directives).validate_timestamps) {
2645        ZCSG(revalidate_at) = zend_accel_get_time() + ZCG(accel_directives).revalidate_freq;
2646    }
2647
2648    /* Override chdir() function */
2649    if (zend_hash_find(CG(function_table), "chdir", sizeof("chdir"), (void**)&func) == SUCCESS &&
2650        func->type == ZEND_INTERNAL_FUNCTION) {
2651        orig_chdir = func->internal_function.handler;
2652        func->internal_function.handler = ZEND_FN(accel_chdir);
2653    }
2654    ZCG(cwd) = NULL;
2655
2656    /* Override "include_path" modifier callback */
2657    if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry) == SUCCESS) {
2658        ZCG(include_path) = INI_STR("include_path");
2659        ZCG(include_path_key) = NULL;
2660        if (ZCG(include_path) && *ZCG(include_path)) {
2661            ZCG(include_path_len) = strlen(ZCG(include_path));
2662            ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1);
2663            if (!ZCG(include_path_key) &&
2664                !zend_accel_hash_is_full(&ZCSG(include_paths))) {
2665                char *key;
2666
2667                zend_shared_alloc_lock(TSRMLS_C);
2668                key = zend_shared_alloc(ZCG(include_path_len) + 2);
2669                if (key) {
2670                    memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1);
2671                    key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries;
2672                    ZCG(include_path_key) = key + ZCG(include_path_len) + 1;
2673                    zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key));
2674                } else {
2675                    zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC);
2676                }
2677                zend_shared_alloc_unlock(TSRMLS_C);
2678            }
2679        } else {
2680            ZCG(include_path) = "";
2681            ZCG(include_path_len) = 0;
2682        }
2683        orig_include_path_on_modify = ini_entry->on_modify;
2684        ini_entry->on_modify = accel_include_path_on_modify;
2685    }
2686
2687    zend_shared_alloc_lock(TSRMLS_C);
2688    zend_shared_alloc_save_state();
2689    zend_shared_alloc_unlock(TSRMLS_C);
2690
2691    SHM_PROTECT();
2692
2693    accel_startup_ok = 1;
2694
2695    /* Override file_exists(), is_file() and is_readable() */
2696    zend_accel_override_file_functions(TSRMLS_C);
2697
2698    /* Load black list */
2699    accel_blacklist.entries = NULL;
2700    if (ZCG(enabled) && accel_startup_ok &&
2701        ZCG(accel_directives).user_blacklist_filename &&
2702        *ZCG(accel_directives.user_blacklist_filename)) {
2703        zend_accel_blacklist_init(&accel_blacklist);
2704        zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
2705    }
2706
2707#if 0
2708    /* FIXME: We probably don't need it here */
2709    zend_accel_copy_internal_functions(TSRMLS_C);
2710#endif
2711
2712    return SUCCESS;
2713}
2714
2715static void accel_free_ts_resources()
2716{
2717#ifndef ZTS
2718    accel_globals_dtor(&accel_globals);
2719#else
2720    ts_free_id(accel_globals_id);
2721#endif
2722}
2723
2724void accel_shutdown(TSRMLS_D)
2725{
2726    zend_ini_entry *ini_entry;
2727
2728    zend_accel_blacklist_shutdown(&accel_blacklist);
2729
2730    if (!ZCG(enabled) || !accel_startup_ok) {
2731        accel_free_ts_resources();
2732        return;
2733    }
2734
2735#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
2736    if (ZCG(accel_directives).interned_strings_buffer) {
2737# ifndef ZTS
2738        zend_hash_clean(CG(function_table));
2739        zend_hash_clean(CG(class_table));
2740        zend_hash_clean(EG(zend_constants));
2741# endif
2742        CG(interned_strings_start) = orig_interned_strings_start;
2743        CG(interned_strings_end) = orig_interned_strings_end;
2744    }
2745    zend_new_interned_string = orig_new_interned_string;
2746    zend_interned_strings_snapshot = orig_interned_strings_snapshot;
2747    zend_interned_strings_restore = orig_interned_strings_restore;
2748#endif
2749
2750    accel_free_ts_resources();
2751    zend_shared_alloc_shutdown();
2752    zend_compile_file = accelerator_orig_compile_file;
2753
2754    if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry) == SUCCESS) {
2755        ini_entry->on_modify = orig_include_path_on_modify;
2756    }
2757}
2758
2759void zend_accel_schedule_restart(zend_accel_restart_reason reason TSRMLS_DC)
2760{
2761    if (ZCSG(restart_pending)) {
2762        /* don't schedule twice */
2763        return;
2764    }
2765    zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!");
2766
2767    SHM_UNPROTECT();
2768    ZCSG(restart_pending) = 1;
2769    ZCSG(restart_reason) = reason;
2770    ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
2771    ZCSG(accelerator_enabled) = 0;
2772
2773    if (ZCG(accel_directives).force_restart_timeout) {
2774        ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
2775    } else {
2776        ZCSG(force_restart_time) = 0;
2777    }
2778    SHM_PROTECT();
2779}
2780
2781/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
2782#ifdef ZEND_WIN32
2783#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub(TSRMLS_C)
2784#else
2785#define accel_deactivate_now() accel_deactivate_sub(TSRMLS_C)
2786#endif
2787
2788/* ensures it is OK to read SHM
2789    if it's not OK (restart in progress) returns FAILURE
2790    if OK returns SUCCESS
2791    MUST call accelerator_shm_read_unlock after done lock operations
2792*/
2793int accelerator_shm_read_lock(TSRMLS_D)
2794{
2795    if (ZCG(counted)) {
2796        /* counted means we are holding read lock for SHM, so that nothing bad can happen */
2797        return SUCCESS;
2798    } else {
2799        /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
2800            or is in progress now */
2801        accel_activate_add(TSRMLS_C); /* acquire usage lock */
2802        /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
2803        if (ZCSG(restart_in_progress)) {
2804            /* we already were inside restart this means it's not safe to touch shm */
2805            accel_deactivate_now(); /* drop usage lock */
2806            return FAILURE;
2807        }
2808    }
2809    return SUCCESS;
2810}
2811
2812/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
2813void accelerator_shm_read_unlock(TSRMLS_D)
2814{
2815    if (!ZCG(counted)) {
2816        /* counted is 0 - meaning we had to readlock manually, release readlock now */
2817        accel_deactivate_now();
2818    }
2819}
2820
2821ZEND_EXT_API zend_extension zend_extension_entry = {
2822    ACCELERATOR_PRODUCT_NAME,               /* name */
2823    ACCELERATOR_VERSION,                    /* version */
2824    "Zend Technologies",                    /* author */
2825    "http://www.zend.com/",                 /* URL */
2826    "Copyright (c) 1999-2014",              /* copyright */
2827    accel_startup,                          /* startup */
2828    NULL,                                   /* shutdown */
2829    accel_activate,                         /* per-script activation */
2830    accel_deactivate,                       /* per-script deactivation */
2831    NULL,                                   /* message handler */
2832    NULL,                                   /* op_array handler */
2833    NULL,                                   /* extended statement handler */
2834    NULL,                                   /* extended fcall begin handler */
2835    NULL,                                   /* extended fcall end handler */
2836    NULL,                                   /* op_array ctor */
2837    NULL,                                   /* op_array dtor */
2838    STANDARD_ZEND_EXTENSION_PROPERTIES
2839};
2840