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