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