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#ifdef HAVE_OPCACHE_FILE_CACHE
1072        if (ZCG(accel_directives).file_cache) {
1073            realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
1074            if (realpath) {
1075                zend_file_cache_invalidate(realpath);
1076                zend_string_release(realpath);
1077            }
1078        }
1079#endif
1080        return FAILURE;
1081    }
1082
1083    realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
1084
1085    if (!realpath) {
1086        return FAILURE;
1087    }
1088
1089#ifdef HAVE_OPCACHE_FILE_CACHE
1090    if (ZCG(accel_directives).file_cache) {
1091        zend_file_cache_invalidate(realpath);
1092    }
1093#endif
1094
1095    persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
1096    if (persistent_script && !persistent_script->corrupted) {
1097        zend_file_handle file_handle;
1098
1099        file_handle.type = ZEND_HANDLE_FILENAME;
1100        file_handle.filename = ZSTR_VAL(realpath);
1101        file_handle.opened_path = realpath;
1102
1103        if (force ||
1104            !ZCG(accel_directives).validate_timestamps ||
1105            do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
1106            SHM_UNPROTECT();
1107            zend_shared_alloc_lock();
1108            if (!persistent_script->corrupted) {
1109                persistent_script->corrupted = 1;
1110                persistent_script->timestamp = 0;
1111                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1112                if (ZSMMG(memory_exhausted)) {
1113                    zend_accel_restart_reason reason =
1114                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1115                    zend_accel_schedule_restart_if_necessary(reason);
1116                }
1117            }
1118            zend_shared_alloc_unlock();
1119            SHM_PROTECT();
1120        }
1121    }
1122
1123    accelerator_shm_read_unlock();
1124    zend_string_release(realpath);
1125
1126    return SUCCESS;
1127}
1128
1129/* Adds another key for existing cached script */
1130static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket)
1131{
1132    if (!zend_accel_hash_str_find(&ZCSG(hash), key, key_length)) {
1133        if (zend_accel_hash_is_full(&ZCSG(hash))) {
1134            zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1135            ZSMMG(memory_exhausted) = 1;
1136            zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1137        } else {
1138            char *new_key = zend_shared_alloc(key_length + 1);
1139            if (new_key) {
1140                memcpy(new_key, key, key_length + 1);
1141                if (zend_accel_hash_update(&ZCSG(hash), new_key, key_length, 1, bucket)) {
1142                    zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", new_key);
1143                }
1144            } else {
1145                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1146            }
1147        }
1148    }
1149}
1150
1151#ifdef HAVE_OPCACHE_FILE_CACHE
1152static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *new_persistent_script, int *from_shared_memory)
1153{
1154    uint memory_used;
1155
1156    /* Check if script may be stored in shared memory */
1157    if (!zend_accel_script_persistable(new_persistent_script)) {
1158        return new_persistent_script;
1159    }
1160
1161    if (!zend_accel_script_optimize(new_persistent_script)) {
1162        return new_persistent_script;
1163    }
1164
1165    zend_shared_alloc_init_xlat_table();
1166
1167    /* Calculate the required memory size */
1168    memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0);
1169
1170    /* Allocate memory block */
1171#ifdef __SSE2__
1172    /* Align to 64-byte boundary */
1173    ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 64);
1174    ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1175#else
1176    ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used);
1177#endif
1178
1179    /* Copy into shared memory */
1180    new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0);
1181
1182    zend_shared_alloc_destroy_xlat_table();
1183
1184    new_persistent_script->is_phar =
1185        new_persistent_script->full_path &&
1186        strstr(ZSTR_VAL(new_persistent_script->full_path), ".phar") &&
1187        !strstr(ZSTR_VAL(new_persistent_script->full_path), "://");
1188
1189    /* Consistency check */
1190    if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1191        zend_accel_error(
1192            ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1193            "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
1194            ZSTR_VAL(new_persistent_script->full_path),
1195            new_persistent_script->mem,
1196            (char *)new_persistent_script->mem + new_persistent_script->size,
1197            ZCG(mem));
1198    }
1199
1200    new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1201
1202    zend_file_cache_script_store(new_persistent_script, 0);
1203
1204    *from_shared_memory = 1;
1205    return new_persistent_script;
1206}
1207#endif
1208
1209static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory)
1210{
1211    zend_accel_hash_entry *bucket;
1212    uint memory_used;
1213
1214    /* Check if script may be stored in shared memory */
1215    if (!zend_accel_script_persistable(new_persistent_script)) {
1216        return new_persistent_script;
1217    }
1218
1219    if (!zend_accel_script_optimize(new_persistent_script)) {
1220        return new_persistent_script;
1221    }
1222
1223    /* exclusive lock */
1224    zend_shared_alloc_lock();
1225
1226    if (zend_accel_hash_is_full(&ZCSG(hash))) {
1227        zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1228        ZSMMG(memory_exhausted) = 1;
1229        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1230        zend_shared_alloc_unlock();
1231        return new_persistent_script;
1232    }
1233
1234    /* Check if we still need to put the file into the cache (may be it was
1235     * already stored by another process. This final check is done under
1236     * exclusive lock) */
1237    bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path);
1238    if (bucket) {
1239        zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1240
1241        if (!existing_persistent_script->corrupted) {
1242            if (key &&
1243                (!ZCG(accel_directives).validate_timestamps ||
1244                 (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
1245                zend_accel_add_key(key, key_length, bucket);
1246            }
1247            zend_shared_alloc_unlock();
1248            return new_persistent_script;
1249        }
1250    }
1251
1252
1253    zend_shared_alloc_init_xlat_table();
1254
1255    /* Calculate the required memory size */
1256    memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length);
1257
1258    /* Allocate shared memory */
1259#ifdef __SSE2__
1260    /* Align to 64-byte boundary */
1261    ZCG(mem) = zend_shared_alloc(memory_used + 64);
1262    ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1263#else
1264    ZCG(mem) = zend_shared_alloc(memory_used);
1265#endif
1266    if (!ZCG(mem)) {
1267        zend_shared_alloc_destroy_xlat_table();
1268        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1269        zend_shared_alloc_unlock();
1270        return new_persistent_script;
1271    }
1272
1273    /* Copy into shared memory */
1274    new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length);
1275
1276    zend_shared_alloc_destroy_xlat_table();
1277
1278    new_persistent_script->is_phar =
1279        new_persistent_script->full_path &&
1280        strstr(ZSTR_VAL(new_persistent_script->full_path), ".phar") &&
1281        !strstr(ZSTR_VAL(new_persistent_script->full_path), "://");
1282
1283    /* Consistency check */
1284    if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1285        zend_accel_error(
1286            ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1287            "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n",
1288            ZSTR_VAL(new_persistent_script->full_path),
1289            new_persistent_script->mem,
1290            (char *)new_persistent_script->mem + new_persistent_script->size,
1291            ZCG(mem));
1292    }
1293
1294    new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1295
1296    /* store script structure in the hash table */
1297    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);
1298    if (bucket) {
1299        zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", new_persistent_script->full_path);
1300        if (key &&
1301            /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1302            memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
1303            (ZSTR_LEN(new_persistent_script->full_path) != key_length ||
1304             memcmp(ZSTR_VAL(new_persistent_script->full_path), key, key_length) != 0)) {
1305            /* link key to the same persistent script in hash table */
1306            if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
1307                zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
1308            } else {
1309                zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1310                ZSMMG(memory_exhausted) = 1;
1311                zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1312            }
1313        }
1314    }
1315
1316    new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1317
1318    zend_shared_alloc_unlock();
1319
1320#ifdef HAVE_OPCACHE_FILE_CACHE
1321    if (ZCG(accel_directives).file_cache) {
1322        SHM_PROTECT();
1323        zend_file_cache_script_store(new_persistent_script, 1);
1324        SHM_UNPROTECT();
1325    }
1326#endif
1327
1328    *from_shared_memory = 1;
1329    return new_persistent_script;
1330}
1331
1332static const struct jit_auto_global_info
1333{
1334    const char *name;
1335    size_t len;
1336} jit_auto_globals_info[] = {
1337    { "_SERVER",  sizeof("_SERVER")-1},
1338    { "_ENV",     sizeof("_ENV")-1},
1339    { "_REQUEST", sizeof("_REQUEST")-1},
1340    { "GLOBALS",  sizeof("GLOBALS")-1},
1341};
1342
1343static zend_string *jit_auto_globals_str[4];
1344
1345static int zend_accel_get_auto_globals(void)
1346{
1347    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1348    int n = 1;
1349    int mask = 0;
1350
1351    for (i = 0; i < ag_size ; i++) {
1352        if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[i])) {
1353            mask |= n;
1354        }
1355        n += n;
1356    }
1357    return mask;
1358}
1359
1360static int zend_accel_get_auto_globals_no_jit(void)
1361{
1362    if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[3])) {
1363        return 8;
1364    }
1365    return 0;
1366}
1367
1368static void zend_accel_set_auto_globals(int mask)
1369{
1370    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1371    int n = 1;
1372
1373    for (i = 0; i < ag_size ; i++) {
1374        if ((mask & n) && !(ZCG(auto_globals_mask) & n)) {
1375            ZCG(auto_globals_mask) |= n;
1376            zend_is_auto_global(jit_auto_globals_str[i]);
1377        }
1378        n += n;
1379    }
1380}
1381
1382static void zend_accel_init_auto_globals(void)
1383{
1384    int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1385
1386    for (i = 0; i < ag_size ; i++) {
1387        jit_auto_globals_str[i] = zend_string_init(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, 1);
1388        zend_string_hash_val(jit_auto_globals_str[i]);
1389        jit_auto_globals_str[i] = accel_new_interned_string(jit_auto_globals_str[i]);
1390    }
1391}
1392
1393static 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)
1394{
1395    zend_persistent_script *new_persistent_script;
1396    zend_op_array *orig_active_op_array;
1397    HashTable *orig_function_table, *orig_class_table;
1398    zval orig_user_error_handler;
1399    zend_op_array *op_array;
1400    int do_bailout = 0;
1401    accel_time_t timestamp = 0;
1402    uint32_t orig_compiler_options = 0;
1403
1404    /* Try to open file */
1405    if (file_handle->type == ZEND_HANDLE_FILENAME) {
1406        if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == SUCCESS) {
1407            /* key may be changed by zend_stream_open_function() */
1408            if (key == ZCG(key)) {
1409                key_length = ZCG(key_len);
1410            }
1411        } else {
1412            *op_array_p = NULL;
1413            if (type == ZEND_REQUIRE) {
1414                zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1415                zend_bailout();
1416            } else {
1417                zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1418            }
1419            return NULL;
1420        }
1421    }
1422
1423    /* check blacklist right after ensuring that file was opened */
1424    if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, ZSTR_VAL(file_handle->opened_path))) {
1425        ZCSG(blacklist_misses)++;
1426        *op_array_p = accelerator_orig_compile_file(file_handle, type);
1427        return NULL;
1428    }
1429
1430    if (ZCG(accel_directives).validate_timestamps ||
1431        ZCG(accel_directives).file_update_protection ||
1432        ZCG(accel_directives).max_file_size > 0) {
1433        size_t size = 0;
1434
1435        /* Obtain the file timestamps, *before* actually compiling them,
1436         * otherwise we have a race-condition.
1437         */
1438        timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL);
1439
1440        /* If we can't obtain a timestamp (that means file is possibly socket)
1441         *  we won't cache it
1442         */
1443        if (timestamp == 0) {
1444            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1445            return NULL;
1446        }
1447
1448        /* check if file is too new (may be it's not written completely yet) */
1449        if (ZCG(accel_directives).file_update_protection &&
1450            (ZCG(request_time) - ZCG(accel_directives).file_update_protection < timestamp)) {
1451            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1452            return NULL;
1453        }
1454
1455        if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1456            ZCSG(blacklist_misses)++;
1457            *op_array_p = accelerator_orig_compile_file(file_handle, type);
1458            return NULL;
1459        }
1460    }
1461
1462    new_persistent_script = create_persistent_script();
1463
1464    /* Save the original values for the op_array, function table and class table */
1465    orig_active_op_array = CG(active_op_array);
1466    orig_function_table = CG(function_table);
1467    orig_class_table = CG(class_table);
1468    ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler));
1469
1470    /* Override them with ours */
1471    CG(function_table) = &ZCG(function_table);
1472    EG(class_table) = CG(class_table) = &new_persistent_script->class_table;
1473    ZVAL_UNDEF(&EG(user_error_handler));
1474
1475    zend_try {
1476        orig_compiler_options = CG(compiler_options);
1477        CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1478        CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1479        CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1480        CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1481        op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1482        CG(compiler_options) = orig_compiler_options;
1483    } zend_catch {
1484        op_array = NULL;
1485        do_bailout = 1;
1486        CG(compiler_options) = orig_compiler_options;
1487    } zend_end_try();
1488
1489    /* Restore originals */
1490    CG(active_op_array) = orig_active_op_array;
1491    CG(function_table) = orig_function_table;
1492    EG(class_table) = CG(class_table) = orig_class_table;
1493    EG(user_error_handler) = orig_user_error_handler;
1494
1495    if (!op_array) {
1496        /* compilation failed */
1497        free_persistent_script(new_persistent_script, 1);
1498        zend_accel_free_user_functions(&ZCG(function_table));
1499        if (do_bailout) {
1500            zend_bailout();
1501        }
1502        return NULL;
1503    }
1504
1505    /* Build the persistent_script structure.
1506       Here we aren't sure we would store it, but we will need it
1507       further anyway.
1508    */
1509    zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table);
1510    new_persistent_script->main_op_array = *op_array;
1511
1512    efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1513
1514    /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1515       will have to ping the used auto global variables before execution */
1516    if (PG(auto_globals_jit)) {
1517        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1518    } else {
1519        new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
1520    }
1521
1522    if (ZCG(accel_directives).validate_timestamps) {
1523        /* Obtain the file timestamps, *before* actually compiling them,
1524         * otherwise we have a race-condition.
1525         */
1526        new_persistent_script->timestamp = timestamp;
1527        new_persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1528    }
1529
1530    if (file_handle->opened_path) {
1531        new_persistent_script->full_path = zend_string_copy(file_handle->opened_path);
1532    } else {
1533        new_persistent_script->full_path = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
1534    }
1535    zend_string_hash_val(new_persistent_script->full_path);
1536
1537    /* Now persistent_script structure is ready in process memory */
1538    return new_persistent_script;
1539}
1540
1541#ifdef HAVE_OPCACHE_FILE_CACHE
1542zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
1543{
1544    zend_persistent_script *persistent_script;
1545    zend_op_array *op_array = NULL;
1546    int from_memory; /* if the script we've got is stored in SHM */
1547
1548    if (is_stream_path(file_handle->filename) &&
1549        !is_cacheable_stream_path(file_handle->filename)) {
1550        return accelerator_orig_compile_file(file_handle, type);
1551    }
1552
1553    if (!file_handle->opened_path) {
1554        if (file_handle->type == ZEND_HANDLE_FILENAME &&
1555            accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1556            if (type == ZEND_REQUIRE) {
1557                zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1558                zend_bailout();
1559            } else {
1560                zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1561            }
1562            return NULL;
1563        }
1564    }
1565
1566    persistent_script = zend_file_cache_script_load(file_handle);
1567    if (persistent_script) {
1568        /* see bug #15471 (old BTS) */
1569        if (persistent_script->full_path) {
1570            if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1571                !EG(current_execute_data)->func ||
1572                !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1573                EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1574                (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1575                 EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1576                if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
1577                    /* ext/phar has to load phar's metadata into memory */
1578                    if (persistent_script->is_phar) {
1579                        php_stream_statbuf ssb;
1580                        char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->full_path));
1581
1582                        memcpy(fname, "phar://", sizeof("phar://") - 1);
1583                        memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path) + 1);
1584                        php_stream_stat_path(fname, &ssb);
1585                        efree(fname);
1586                    }
1587                }
1588            }
1589        }
1590        zend_file_handle_dtor(file_handle);
1591
1592        persistent_script->dynamic_members.last_used = ZCG(request_time);
1593
1594        if (persistent_script->ping_auto_globals_mask) {
1595            zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
1596        }
1597
1598        return zend_accel_load_script(persistent_script, 1);
1599    }
1600
1601    persistent_script = opcache_compile_file(file_handle, type, NULL, 0, &op_array);
1602
1603    if (persistent_script) {
1604        from_memory = 0;
1605        persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
1606        return zend_accel_load_script(persistent_script, from_memory);
1607    }
1608
1609    return op_array;
1610}
1611#endif
1612
1613/* zend_compile() replacement */
1614zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
1615{
1616    zend_persistent_script *persistent_script = NULL;
1617    char *key = NULL;
1618    int key_length;
1619    int from_shared_memory; /* if the script we've got is stored in SHM */
1620
1621    if (!file_handle->filename || !ZCG(enabled) || !accel_startup_ok) {
1622        /* The Accelerator is disabled, act as if without the Accelerator */
1623        return accelerator_orig_compile_file(file_handle, type);
1624#ifdef HAVE_OPCACHE_FILE_CACHE
1625    } else if (ZCG(accel_directives).file_cache_only) {
1626        return file_cache_compile_file(file_handle, type);
1627#endif
1628    } else if ((!ZCG(counted) && !ZCSG(accelerator_enabled)) ||
1629               (ZCSG(restart_in_progress) && accel_restart_is_active())) {
1630#ifdef HAVE_OPCACHE_FILE_CACHE
1631        if (ZCG(accel_directives).file_cache) {
1632            return file_cache_compile_file(file_handle, type);
1633        }
1634#endif
1635        return accelerator_orig_compile_file(file_handle, type);
1636    }
1637
1638    /* In case this callback is called from include_once, require_once or it's
1639     * a main FastCGI request, the key must be already calculated, and cached
1640     * persistent script already found */
1641    if (ZCG(cache_persistent_script) &&
1642        ((!EG(current_execute_data) &&
1643          file_handle->filename == SG(request_info).path_translated &&
1644          ZCG(cache_opline) == NULL) ||
1645         (EG(current_execute_data) &&
1646          EG(current_execute_data)->func &&
1647          ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1648          ZCG(cache_opline) == EG(current_execute_data)->opline))) {
1649
1650        persistent_script = ZCG(cache_persistent_script);
1651        if (ZCG(key_len)) {
1652            key = ZCG(key);
1653            key_length = ZCG(key_len);
1654        }
1655
1656    } else {
1657        if (!ZCG(accel_directives).revalidate_path) {
1658            /* try to find cached script by key */
1659            key = accel_make_persistent_key(file_handle->filename, strlen(file_handle->filename), &key_length);
1660            if (!key) {
1661                return accelerator_orig_compile_file(file_handle, type);
1662            }
1663            persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length);
1664        }
1665        if (!persistent_script) {
1666            /* try to find cached script by full real path */
1667            zend_accel_hash_entry *bucket;
1668
1669            /* open file to resolve the path */
1670            if (file_handle->type == ZEND_HANDLE_FILENAME &&
1671                accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1672                if (type == ZEND_REQUIRE) {
1673                    zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1674                    zend_bailout();
1675                } else {
1676                    zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1677                }
1678                return NULL;
1679            }
1680
1681            if (file_handle->opened_path) {
1682                bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path);
1683
1684                if (bucket) {
1685                    persistent_script = (zend_persistent_script *)bucket->data;
1686
1687                    if (key && !persistent_script->corrupted) {
1688                        SHM_UNPROTECT();
1689                        zend_shared_alloc_lock();
1690                        zend_accel_add_key(key, key_length, bucket);
1691                        zend_shared_alloc_unlock();
1692                        SHM_PROTECT();
1693                    }
1694                }
1695            }
1696        }
1697    }
1698
1699    /* clear cache */
1700    ZCG(cache_opline) = NULL;
1701    ZCG(cache_persistent_script) = NULL;
1702
1703    if (persistent_script && persistent_script->corrupted) {
1704        persistent_script = NULL;
1705    }
1706
1707    /* Make sure we only increase the currently running processes semaphore
1708     * once each execution (this function can be called more than once on
1709     * each execution)
1710     */
1711    if (!ZCG(counted)) {
1712        ZCG(counted) = 1;
1713        accel_activate_add();
1714    }
1715
1716    SHM_UNPROTECT();
1717
1718    /* If script is found then validate_timestamps if option is enabled */
1719    if (persistent_script && ZCG(accel_directives).validate_timestamps) {
1720        if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
1721            zend_shared_alloc_lock();
1722            if (!persistent_script->corrupted) {
1723                persistent_script->corrupted = 1;
1724                persistent_script->timestamp = 0;
1725                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1726                if (ZSMMG(memory_exhausted)) {
1727                    zend_accel_restart_reason reason =
1728                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1729                    zend_accel_schedule_restart_if_necessary(reason);
1730                }
1731            }
1732            zend_shared_alloc_unlock();
1733            persistent_script = NULL;
1734        }
1735    }
1736
1737    /* if turned on - check the compiled script ADLER32 checksum */
1738    if (persistent_script && ZCG(accel_directives).consistency_checks
1739        && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
1740
1741        unsigned int checksum = zend_accel_script_checksum(persistent_script);
1742        if (checksum != persistent_script->dynamic_members.checksum ) {
1743            /* The checksum is wrong */
1744            zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s':  expected=0x%0.8X, found=0x%0.8X",
1745                             persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum);
1746            zend_shared_alloc_lock();
1747            if (!persistent_script->corrupted) {
1748                persistent_script->corrupted = 1;
1749                persistent_script->timestamp = 0;
1750                ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1751                if (ZSMMG(memory_exhausted)) {
1752                    zend_accel_restart_reason reason =
1753                        zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1754                    zend_accel_schedule_restart_if_necessary(reason);
1755                }
1756            }
1757            zend_shared_alloc_unlock();
1758            persistent_script = NULL;
1759        }
1760    }
1761
1762#ifdef HAVE_OPCACHE_FILE_CACHE
1763    /* Check the second level cache */
1764    if (!persistent_script && ZCG(accel_directives).file_cache) {
1765        persistent_script = zend_file_cache_script_load(file_handle);
1766    }
1767#endif
1768
1769    /* If script was not found or invalidated by validate_timestamps */
1770    if (!persistent_script) {
1771        uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
1772        zend_op_array *op_array;
1773
1774        /* Cache miss.. */
1775        ZCSG(misses)++;
1776
1777        /* No memory left. Behave like without the Accelerator */
1778        if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
1779            SHM_PROTECT();
1780            return accelerator_orig_compile_file(file_handle, type);
1781        }
1782
1783        /* Try and cache the script and assume that it is returned from_shared_memory.
1784         * If it isn't compile_and_cache_file() changes the flag to 0
1785         */
1786        from_shared_memory = 0;
1787        persistent_script = opcache_compile_file(file_handle, type, key, key ? key_length : 0, &op_array);
1788        if (persistent_script) {
1789            persistent_script = cache_script_in_shared_memory(persistent_script, key, key ? key_length : 0, &from_shared_memory);
1790        }
1791
1792        /* Caching is disabled, returning op_array;
1793         * or something went wrong during compilation, returning NULL
1794         */
1795        if (!persistent_script) {
1796            SHM_PROTECT();
1797            return op_array;
1798        }
1799        if (from_shared_memory) {
1800            /* Delete immutable arrays moved into SHM */
1801            uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
1802            while (new_const_num > old_const_num) {
1803                new_const_num--;
1804                zend_hash_index_del(EG(zend_constants), new_const_num);
1805            }
1806        }
1807    } else {
1808
1809#if !ZEND_WIN32
1810        ZCSG(hits)++; /* TBFixed: may lose one hit */
1811        persistent_script->dynamic_members.hits++; /* see above */
1812#else
1813        INCREMENT(hits);
1814        InterlockedIncrement64(&persistent_script->dynamic_members.hits);
1815#endif
1816
1817        /* see bug #15471 (old BTS) */
1818        if (persistent_script->full_path) {
1819            if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1820                !EG(current_execute_data)->func ||
1821                !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1822                EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1823                (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1824                 EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1825                if (zend_hash_add_empty_element(&EG(included_files), persistent_script->full_path) != NULL) {
1826                    /* ext/phar has to load phar's metadata into memory */
1827                    if (persistent_script->is_phar) {
1828                        php_stream_statbuf ssb;
1829                        char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->full_path));
1830
1831                        memcpy(fname, "phar://", sizeof("phar://") - 1);
1832                        memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->full_path), ZSTR_LEN(persistent_script->full_path) + 1);
1833                        php_stream_stat_path(fname, &ssb);
1834                        efree(fname);
1835                    }
1836                }
1837            }
1838        }
1839        zend_file_handle_dtor(file_handle);
1840        from_shared_memory = 1;
1841    }
1842
1843    persistent_script->dynamic_members.last_used = ZCG(request_time);
1844
1845    SHM_PROTECT();
1846
1847    /* Fetch jit auto globals used in the script before execution */
1848    if (persistent_script->ping_auto_globals_mask) {
1849        zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
1850    }
1851
1852    return zend_accel_load_script(persistent_script, from_shared_memory);
1853}
1854
1855/* zend_stream_open_function() replacement for PHP 5.3 and above */
1856static int persistent_stream_open_function(const char *filename, zend_file_handle *handle)
1857{
1858    if (ZCG(cache_persistent_script)) {
1859        /* check if callback is called from include_once or it's a main request */
1860        if ((!EG(current_execute_data) &&
1861             filename == SG(request_info).path_translated &&
1862             ZCG(cache_opline) == NULL) ||
1863            (EG(current_execute_data) &&
1864             EG(current_execute_data)->func &&
1865             ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1866             ZCG(cache_opline) == EG(current_execute_data)->opline)) {
1867
1868            /* we are in include_once or FastCGI request */
1869            handle->filename = (char*)filename;
1870            handle->free_filename = 0;
1871            handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->full_path);
1872            handle->type = ZEND_HANDLE_FILENAME;
1873            return SUCCESS;
1874        }
1875        ZCG(cache_opline) = NULL;
1876        ZCG(cache_persistent_script) = NULL;
1877    }
1878    return accelerator_orig_zend_stream_open_function(filename, handle);
1879}
1880
1881/* zend_resolve_path() replacement for PHP 5.3 and above */
1882static zend_string* persistent_zend_resolve_path(const char *filename, int filename_len)
1883{
1884    if (ZCG(enabled) && accel_startup_ok &&
1885        (ZCG(counted) || ZCSG(accelerator_enabled)) &&
1886        !ZCSG(restart_in_progress)) {
1887
1888        /* check if callback is called from include_once or it's a main request */
1889        if ((!EG(current_execute_data) &&
1890             filename == SG(request_info).path_translated) ||
1891            (EG(current_execute_data) &&
1892             EG(current_execute_data)->func &&
1893             ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1894             EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
1895             (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
1896              EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
1897
1898            /* we are in include_once or FastCGI request */
1899            zend_string *resolved_path;
1900            int key_length;
1901            char *key = NULL;
1902
1903            if (!ZCG(accel_directives).revalidate_path) {
1904                /* lookup by "not-real" path */
1905                key = accel_make_persistent_key(filename, filename_len, &key_length);
1906                if (key) {
1907                    zend_accel_hash_entry *bucket = zend_accel_hash_str_find_entry(&ZCSG(hash), key, key_length);
1908                    if (bucket != NULL) {
1909                        zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
1910                        if (!persistent_script->corrupted) {
1911                            ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1912                            ZCG(cache_persistent_script) = persistent_script;
1913                            return zend_string_copy(persistent_script->full_path);
1914                        }
1915                    }
1916                } else {
1917                    ZCG(cache_opline) = NULL;
1918                    ZCG(cache_persistent_script) = NULL;
1919                    return accelerator_orig_zend_resolve_path(filename, filename_len);
1920                }
1921            }
1922
1923            /* find the full real path */
1924            resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len);
1925
1926            if (resolved_path) {
1927                /* lookup by real path */
1928                zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path);
1929                if (bucket) {
1930                    zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
1931                    if (!persistent_script->corrupted) {
1932                        if (key) {
1933                            /* add another "key" for the same bucket */
1934                            SHM_UNPROTECT();
1935                            zend_shared_alloc_lock();
1936                            zend_accel_add_key(key, key_length, bucket);
1937                            zend_shared_alloc_unlock();
1938                            SHM_PROTECT();
1939                        } else {
1940                            ZCG(key_len) = 0;
1941                        }
1942                        ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
1943                        ZCG(cache_persistent_script) = persistent_script;
1944                        return resolved_path;
1945                    }
1946                }
1947            }
1948
1949            ZCG(cache_opline) = NULL;
1950            ZCG(cache_persistent_script) = NULL;
1951            return resolved_path;
1952        }
1953    }
1954    ZCG(cache_opline) = NULL;
1955    ZCG(cache_persistent_script) = NULL;
1956    return accelerator_orig_zend_resolve_path(filename, filename_len);
1957}
1958
1959static void zend_reset_cache_vars(void)
1960{
1961    ZSMMG(memory_exhausted) = 0;
1962    ZCSG(hits) = 0;
1963    ZCSG(misses) = 0;
1964    ZCSG(blacklist_misses) = 0;
1965    ZSMMG(wasted_shared_memory) = 0;
1966    ZCSG(restart_pending) = 0;
1967    ZCSG(force_restart_time) = 0;
1968}
1969
1970static void accel_reset_pcre_cache(void)
1971{
1972    Bucket *p;
1973
1974    ZEND_HASH_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) {
1975        /* Remove PCRE cache entries with inconsistent keys */
1976        if (ZSTR_IS_INTERNED(p->key)) {
1977            p->key = NULL;
1978            zend_hash_del_bucket(&PCRE_G(pcre_cache), p);
1979        }
1980    } ZEND_HASH_FOREACH_END();
1981}
1982
1983static void accel_activate(void)
1984{
1985    zend_bool reset_pcre = 0;
1986
1987    if (!ZCG(enabled) || !accel_startup_ok) {
1988        return;
1989    }
1990
1991    if (!ZCG(function_table).nTableSize) {
1992        zend_hash_init(&ZCG(function_table), zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1);
1993        zend_accel_copy_internal_functions();
1994    }
1995
1996    /* PHP-5.4 and above return "double", but we use 1 sec precision */
1997    ZCG(auto_globals_mask) = 0;
1998    ZCG(request_time) = (time_t)sapi_get_request_time();
1999    ZCG(cache_opline) = NULL;
2000    ZCG(cache_persistent_script) = NULL;
2001    ZCG(include_path_key_len) = 0;
2002    ZCG(include_path_check) = 1;
2003
2004    /* check if ZCG(function_table) wasn't somehow polluted on the way */
2005    if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) {
2006        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)));
2007    }
2008
2009    ZCG(cwd) = NULL;
2010    ZCG(cwd_key_len) = 0;
2011    ZCG(cwd_check) = 1;
2012
2013#ifdef HAVE_OPCACHE_FILE_CACHE
2014    if (ZCG(accel_directives).file_cache_only) {
2015        return;
2016    }
2017#endif
2018
2019    SHM_UNPROTECT();
2020
2021    if (ZCG(counted)) {
2022#ifdef ZTS
2023        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id());
2024#else
2025        zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
2026#endif
2027        accel_unlock_all();
2028        ZCG(counted) = 0;
2029    }
2030
2031    if (ZCSG(restart_pending)) {
2032        zend_shared_alloc_lock();
2033        if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
2034            if (accel_is_inactive() == SUCCESS) {
2035                zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
2036                ZCSG(restart_pending) = 0;
2037                switch ZCSG(restart_reason) {
2038                    case ACCEL_RESTART_OOM:
2039                        ZCSG(oom_restarts)++;
2040                        break;
2041                    case ACCEL_RESTART_HASH:
2042                        ZCSG(hash_restarts)++;
2043                        break;
2044                    case ACCEL_RESTART_USER:
2045                        ZCSG(manual_restarts)++;
2046                        break;
2047                }
2048                accel_restart_enter();
2049
2050                zend_reset_cache_vars();
2051                zend_accel_hash_clean(&ZCSG(hash));
2052
2053#if !defined(ZTS)
2054                if (ZCG(accel_directives).interned_strings_buffer) {
2055                    accel_interned_strings_restore_state();
2056                }
2057#endif
2058
2059                zend_shared_alloc_restore_state();
2060                ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
2061                if (ZCSG(last_restart_time) < ZCG(request_time)) {
2062                    ZCSG(last_restart_time) = ZCG(request_time);
2063                } else {
2064                    ZCSG(last_restart_time)++;
2065                }
2066                accel_restart_leave();
2067            }
2068        } else {
2069            reset_pcre = 1;
2070        }
2071        zend_shared_alloc_unlock();
2072    }
2073
2074    SHM_PROTECT();
2075
2076    if (ZCSG(last_restart_time) != ZCG(last_restart_time)) {
2077        /* SHM was reinitialized. */
2078        ZCG(last_restart_time) = ZCSG(last_restart_time);
2079
2080        /* Reset in-process realpath cache */
2081        realpath_cache_clean();
2082
2083        accel_reset_pcre_cache();
2084    } else if (reset_pcre) {
2085        accel_reset_pcre_cache();
2086    }
2087}
2088
2089#if !ZEND_DEBUG
2090
2091/* Fast Request Shutdown
2092 * =====================
2093 * Zend Memory Manager frees memory by its own. We don't have to free each
2094 * allocated block separately, but we like to call all the destructors and
2095 * callbacks in exactly the same order.
2096 */
2097static void accel_fast_hash_destroy(HashTable *ht);
2098
2099static void accel_fast_zval_dtor(zval *zvalue)
2100{
2101tail_call:
2102    switch (Z_TYPE_P(zvalue)) {
2103        case IS_ARRAY:
2104            GC_REMOVE_FROM_BUFFER(Z_ARR_P(zvalue));
2105            if (Z_ARR_P(zvalue) != &EG(symbol_table)) {
2106                /* break possible cycles */
2107                ZVAL_NULL(zvalue);
2108                accel_fast_hash_destroy(Z_ARRVAL_P(zvalue));
2109            }
2110            break;
2111        case IS_OBJECT:
2112            OBJ_RELEASE(Z_OBJ_P(zvalue));
2113            break;
2114        case IS_RESOURCE:
2115            zend_list_delete(Z_RES_P(zvalue));
2116            break;
2117        case IS_REFERENCE: {
2118                zend_reference *ref = Z_REF_P(zvalue);
2119
2120                if (--GC_REFCOUNT(ref) == 0) {
2121                    if (Z_REFCOUNTED(ref->val) && Z_DELREF(ref->val) == 0) {
2122                        zvalue = &ref->val;
2123                        goto tail_call;
2124                    }
2125                }
2126            }
2127            break;
2128    }
2129}
2130
2131static void accel_fast_hash_destroy(HashTable *ht)
2132{
2133    Bucket *p = ht->arData;
2134    Bucket *end = p + ht->nNumUsed;
2135
2136    while (p != end) {
2137        if (Z_REFCOUNTED(p->val) && Z_DELREF(p->val) == 0) {
2138            accel_fast_zval_dtor(&p->val);
2139        }
2140        p++;
2141    }
2142}
2143
2144static inline void zend_accel_fast_del_bucket(HashTable *ht, uint32_t idx, Bucket *p)
2145{
2146    uint32_t nIndex = p->h | ht->nTableMask;
2147    uint32_t i = HT_HASH(ht, nIndex);
2148
2149    ht->nNumOfElements--;
2150    if (idx != i) {
2151        Bucket *prev = HT_HASH_TO_BUCKET(ht, i);
2152        while (Z_NEXT(prev->val) != idx) {
2153            i = Z_NEXT(prev->val);
2154            prev = HT_HASH_TO_BUCKET(ht, i);
2155        }
2156        Z_NEXT(prev->val) = Z_NEXT(p->val);
2157    } else {
2158        HT_HASH(ht, p->h | ht->nTableMask) = Z_NEXT(p->val);
2159    }
2160}
2161
2162static void zend_accel_fast_shutdown(void)
2163{
2164    if (EG(full_tables_cleanup)) {
2165        return;
2166    }
2167
2168    if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) {
2169        /* We don't have to destroy all zvals if they cannot call any destructors */
2170        zend_try {
2171            ZEND_HASH_REVERSE_FOREACH(&EG(symbol_table), 0) {
2172                if (Z_REFCOUNTED(_p->val) && Z_DELREF(_p->val) == 0) {
2173                    accel_fast_zval_dtor(&_p->val);
2174                }
2175                zend_accel_fast_del_bucket(&EG(symbol_table), HT_IDX_TO_HASH(_idx-1), _p);
2176            } ZEND_HASH_FOREACH_END();
2177        } zend_end_try();
2178        zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0);
2179
2180        ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) {
2181            zend_function *func = Z_PTR(_p->val);
2182
2183            if (func->type == ZEND_INTERNAL_FUNCTION) {
2184                break;
2185            } else {
2186                if (func->op_array.static_variables) {
2187                    if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
2188                        if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
2189                            accel_fast_hash_destroy(func->op_array.static_variables);
2190                        }
2191                    }
2192                }
2193                zend_accel_fast_del_bucket(EG(function_table), HT_IDX_TO_HASH(_idx-1), _p);
2194            }
2195        } ZEND_HASH_FOREACH_END();
2196
2197        ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) {
2198            zend_class_entry *ce = Z_PTR(_p->val);
2199
2200            if (ce->type == ZEND_INTERNAL_CLASS) {
2201                break;
2202            } else {
2203                if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
2204                    zend_function *func;
2205
2206                    ZEND_HASH_FOREACH_PTR(&ce->function_table, func) {
2207                        if (func->type == ZEND_USER_FUNCTION) {
2208                            if (func->op_array.static_variables) {
2209                                if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) {
2210                                    if (--GC_REFCOUNT(func->op_array.static_variables) == 0) {
2211                                        accel_fast_hash_destroy(func->op_array.static_variables);
2212                                    }
2213                                }
2214                                func->op_array.static_variables = NULL;
2215                            }
2216                        }
2217                    } ZEND_HASH_FOREACH_END();
2218                }
2219                if (ce->static_members_table) {
2220                    int i;
2221
2222                    for (i = 0; i < ce->default_static_members_count; i++) {
2223                        zval *zv = &ce->static_members_table[i];
2224                        ZVAL_UNDEF(&ce->static_members_table[i]);
2225                        if (Z_REFCOUNTED_P(zv) && Z_DELREF_P(zv) == 0) {
2226                            accel_fast_zval_dtor(zv);
2227                        }
2228                    }
2229                    ce->static_members_table = NULL;
2230                }
2231                zend_accel_fast_del_bucket(EG(class_table), HT_IDX_TO_HASH(_idx-1), _p);
2232            }
2233        } ZEND_HASH_FOREACH_END();
2234
2235    } else {
2236
2237        zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0);
2238
2239        ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) {
2240            zend_function *func = Z_PTR(_p->val);
2241
2242            if (func->type == ZEND_INTERNAL_FUNCTION) {
2243                break;
2244            } else {
2245                zend_accel_fast_del_bucket(EG(function_table), HT_IDX_TO_HASH(_idx-1), _p);
2246            }
2247        } ZEND_HASH_FOREACH_END();
2248
2249        ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) {
2250            zend_class_entry *ce = Z_PTR(_p->val);
2251
2252            if (ce->type == ZEND_INTERNAL_CLASS) {
2253                break;
2254            } else {
2255                zend_accel_fast_del_bucket(EG(class_table), HT_IDX_TO_HASH(_idx-1), _p);
2256            }
2257        } ZEND_HASH_FOREACH_END();
2258    }
2259
2260    ZEND_HASH_REVERSE_FOREACH(EG(zend_constants), 0) {
2261        zend_constant *c = Z_PTR(_p->val);
2262
2263        if (c->flags & CONST_PERSISTENT) {
2264            break;
2265        } else {
2266            zend_accel_fast_del_bucket(EG(zend_constants), HT_IDX_TO_HASH(_idx-1), _p);
2267        }
2268    } ZEND_HASH_FOREACH_END();
2269    EG(function_table)->nNumUsed = EG(function_table)->nNumOfElements;
2270    EG(class_table)->nNumUsed = EG(class_table)->nNumOfElements;
2271    EG(zend_constants)->nNumUsed = EG(zend_constants)->nNumOfElements;
2272
2273    CG(unclean_shutdown) = 1;
2274}
2275#endif
2276
2277static void accel_deactivate(void)
2278{
2279    /* ensure that we restore function_table and class_table
2280     * In general, they're restored by persistent_compile_file(), but in case
2281     * the script is aborted abnormally, they may become messed up.
2282     */
2283
2284    if (!ZCG(enabled) || !accel_startup_ok) {
2285        return;
2286    }
2287
2288    zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
2289    accel_unlock_all();
2290    ZCG(counted) = 0;
2291
2292#if !ZEND_DEBUG
2293    if (ZCG(accel_directives).fast_shutdown) {
2294        zend_accel_fast_shutdown();
2295    }
2296#endif
2297
2298    if (ZCG(cwd)) {
2299        zend_string_release(ZCG(cwd));
2300        ZCG(cwd) = NULL;
2301    }
2302
2303}
2304
2305static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
2306{
2307    (void)element2; /* keep the compiler happy */
2308
2309    if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
2310        element1->startup = NULL;
2311#if 0
2312        /* We have to call shutdown callback it to free TS resources */
2313        element1->shutdown = NULL;
2314#endif
2315        element1->activate = NULL;
2316        element1->deactivate = NULL;
2317        element1->op_array_handler = NULL;
2318
2319#ifdef __DEBUG_MESSAGES__
2320        fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
2321        fflush(stderr);
2322#endif
2323    }
2324
2325    return 0;
2326}
2327
2328static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *))
2329{
2330    accel_startup_ok = 0;
2331    zps_failure_reason = reason;
2332    zps_api_failure_reason = api_reason?api_reason:reason;
2333    zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
2334}
2335
2336static inline int accel_find_sapi(void)
2337{
2338    static const char *supported_sapis[] = {
2339        "apache",
2340        "fastcgi",
2341        "cli-server",
2342        "cgi-fcgi",
2343        "fpm-fcgi",
2344        "isapi",
2345        "apache2filter",
2346        "apache2handler",
2347        "litespeed",
2348        NULL
2349    };
2350    const char **sapi_name;
2351
2352    if (sapi_module.name) {
2353        for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
2354            if (strcmp(sapi_module.name, *sapi_name) == 0) {
2355                return SUCCESS;
2356            }
2357        }
2358        if (ZCG(accel_directives).enable_cli && (
2359            strcmp(sapi_module.name, "cli") == 0
2360          || strcmp(sapi_module.name, "phpdbg") == 0)) {
2361            return SUCCESS;
2362        }
2363    }
2364
2365    return FAILURE;
2366}
2367
2368static int zend_accel_init_shm(void)
2369{
2370    zend_shared_alloc_lock();
2371
2372    accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals));
2373    if (!accel_shared_globals) {
2374        zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2375        return FAILURE;
2376    }
2377    ZSMMG(app_shared_globals) = accel_shared_globals;
2378
2379    zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
2380
2381    ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL;
2382# ifndef ZTS
2383    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);
2384    if (ZCG(accel_directives).interned_strings_buffer) {
2385        void *data;
2386
2387        ZCSG(interned_strings).nTableMask = -ZCSG(interned_strings).nTableSize;
2388        data = zend_shared_alloc(HT_SIZE(&ZCSG(interned_strings)));
2389        ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2390        if (!data || !ZCSG(interned_strings_start)) {
2391            zend_accel_error(ACCEL_LOG_FATAL, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings");
2392            return FAILURE;
2393        }
2394        HT_SET_DATA_ADDR(&ZCSG(interned_strings), data);
2395        HT_HASH_RESET(&ZCSG(interned_strings));
2396        ZCSG(interned_strings_end)   = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2397        ZCSG(interned_strings_top)   = ZCSG(interned_strings_start);
2398
2399//      orig_interned_strings_start = CG(interned_strings_start);
2400//      orig_interned_strings_end = CG(interned_strings_end);
2401//      CG(interned_strings_start) = ZCSG(interned_strings_start);
2402//      CG(interned_strings_end) = ZCSG(interned_strings_end);
2403    }
2404# endif
2405
2406    orig_new_interned_string = zend_new_interned_string;
2407    orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2408    orig_interned_strings_restore = zend_interned_strings_restore;
2409    zend_new_interned_string = accel_new_interned_string_for_php;
2410    zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2411    zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2412
2413# ifndef ZTS
2414    if (ZCG(accel_directives).interned_strings_buffer) {
2415        accel_use_shm_interned_strings();
2416        accel_interned_strings_save_state();
2417    }
2418# endif
2419
2420    zend_reset_cache_vars();
2421
2422    ZCSG(oom_restarts) = 0;
2423    ZCSG(hash_restarts) = 0;
2424    ZCSG(manual_restarts) = 0;
2425
2426    ZCSG(accelerator_enabled) = 1;
2427    ZCSG(start_time) = zend_accel_get_time();
2428    ZCSG(last_restart_time) = 0;
2429    ZCSG(restart_in_progress) = 0;
2430
2431    zend_shared_alloc_unlock();
2432
2433    return SUCCESS;
2434}
2435
2436static void accel_globals_ctor(zend_accel_globals *accel_globals)
2437{
2438#if defined(COMPILE_DL_OPCACHE) && defined(ZTS)
2439    ZEND_TSRMLS_CACHE_UPDATE();
2440#endif
2441    memset(accel_globals, 0, sizeof(zend_accel_globals));
2442}
2443
2444static void accel_globals_internal_func_dtor(zval *zv)
2445{
2446    free(Z_PTR_P(zv));
2447}
2448
2449static void accel_globals_dtor(zend_accel_globals *accel_globals)
2450{
2451    if (accel_globals->function_table.nTableSize) {
2452        accel_globals->function_table.pDestructor = accel_globals_internal_func_dtor;
2453        zend_hash_destroy(&accel_globals->function_table);
2454    }
2455}
2456
2457#ifdef HAVE_OPCACHE_FILE_CACHE
2458
2459#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)
2460
2461static void accel_gen_system_id(void)
2462{
2463    PHP_MD5_CTX context;
2464    unsigned char digest[16], c;
2465    char *md5str = ZCG(system_id);
2466    int i;
2467
2468    PHP_MD5Init(&context);
2469    PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
2470    PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
2471    PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
2472    if (strstr(PHP_VERSION, "-dev") != 0) {
2473        /* Development versions may be changed from build to build */
2474        PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
2475        PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
2476    }
2477    PHP_MD5Final(digest, &context);
2478    for (i = 0; i < 16; i++) {
2479        c = digest[i] >> 4;
2480        c = (c <= 9) ? c + '0' : c - 10 + 'a';
2481        md5str[i * 2] = c;
2482        c = digest[i] &  0x0f;
2483        c = (c <= 9) ? c + '0' : c - 10 + 'a';
2484        md5str[(i * 2) + 1] = c;
2485    }
2486}
2487#endif
2488
2489static int accel_startup(zend_extension *extension)
2490{
2491    zend_function *func;
2492    zend_ini_entry *ini_entry;
2493
2494#ifdef ZTS
2495    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);
2496#else
2497    accel_globals_ctor(&accel_globals);
2498#endif
2499
2500#ifdef ZEND_WIN32
2501    _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
2502#endif
2503
2504    if (start_accel_module() == FAILURE) {
2505        accel_startup_ok = 0;
2506        zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
2507        return FAILURE;
2508    }
2509
2510#ifdef HAVE_OPCACHE_FILE_CACHE
2511    accel_gen_system_id();
2512#endif
2513
2514    /* no supported SAPI found - disable acceleration and stop initialization */
2515    if (accel_find_sapi() == FAILURE) {
2516        accel_startup_ok = 0;
2517        if (!ZCG(accel_directives).enable_cli &&
2518            strcmp(sapi_module.name, "cli") == 0) {
2519            zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
2520        } else {
2521            zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI, FPM, FastCGI and LiteSpeed SAPIs", NULL, accelerator_remove_cb);
2522        }
2523        return SUCCESS;
2524    }
2525
2526    if (ZCG(enabled) == 0) {
2527        return SUCCESS ;
2528    }
2529
2530/********************************************/
2531/* End of non-SHM dependent initializations */
2532/********************************************/
2533#ifdef HAVE_OPCACHE_FILE_CACHE
2534    if (!ZCG(accel_directives).file_cache_only) {
2535#else
2536    if (1) {
2537#endif
2538        switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
2539            case ALLOC_SUCCESS:
2540                if (zend_accel_init_shm() == FAILURE) {
2541                    accel_startup_ok = 0;
2542                    return FAILURE;
2543                }
2544                break;
2545            case ALLOC_FAILURE:
2546                accel_startup_ok = 0;
2547                zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
2548                return SUCCESS;
2549            case SUCCESSFULLY_REATTACHED:
2550                accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
2551                zend_shared_alloc_lock();
2552                orig_new_interned_string = zend_new_interned_string;
2553                orig_interned_strings_snapshot = zend_interned_strings_snapshot;
2554                orig_interned_strings_restore = zend_interned_strings_restore;
2555
2556                zend_new_interned_string = accel_new_interned_string_for_php;
2557                zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php;
2558                zend_interned_strings_restore = accel_interned_strings_restore_for_php;
2559#ifndef ZTS
2560                accel_use_shm_interned_strings();
2561#endif
2562                zend_shared_alloc_unlock();
2563                break;
2564            case FAILED_REATTACHED:
2565                accel_startup_ok = 0;
2566                zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
2567                return SUCCESS;
2568                break;
2569        }
2570
2571        /* from this point further, shared memory is supposed to be OK */
2572
2573        /* remeber the last restart time in the process memory */
2574        ZCG(last_restart_time) = ZCSG(last_restart_time);
2575
2576        /* Init auto-global strings */
2577        zend_accel_init_auto_globals();
2578
2579        zend_shared_alloc_lock();
2580        zend_shared_alloc_save_state();
2581        zend_shared_alloc_unlock();
2582
2583        SHM_PROTECT();
2584#ifdef HAVE_OPCACHE_FILE_CACHE
2585    } else if (!ZCG(accel_directives).file_cache) {
2586        accel_startup_ok = 0;
2587        zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
2588        return SUCCESS;
2589    } else {
2590        accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
2591
2592        /* Init auto-global strings */
2593        zend_accel_init_auto_globals();
2594#endif
2595    }
2596
2597    /* Override compiler */
2598    accelerator_orig_compile_file = zend_compile_file;
2599    zend_compile_file = persistent_compile_file;
2600
2601    /* Override stream opener function (to eliminate open() call caused by
2602     * include/require statements ) */
2603    accelerator_orig_zend_stream_open_function = zend_stream_open_function;
2604    zend_stream_open_function = persistent_stream_open_function;
2605
2606    /* Override path resolver function (to eliminate stat() calls caused by
2607     * include_once/require_once statements */
2608    accelerator_orig_zend_resolve_path = zend_resolve_path;
2609    zend_resolve_path = persistent_zend_resolve_path;
2610
2611    /* Override chdir() function */
2612    if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
2613        func->type == ZEND_INTERNAL_FUNCTION) {
2614        orig_chdir = func->internal_function.handler;
2615        func->internal_function.handler = ZEND_FN(accel_chdir);
2616    }
2617    ZCG(cwd) = NULL;
2618    ZCG(include_path) = NULL;
2619
2620    /* Override "include_path" modifier callback */
2621    if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
2622        ZCG(include_path) = ini_entry->value;
2623        orig_include_path_on_modify = ini_entry->on_modify;
2624        ini_entry->on_modify = accel_include_path_on_modify;
2625    }
2626
2627    accel_startup_ok = 1;
2628
2629    /* Override file_exists(), is_file() and is_readable() */
2630    zend_accel_override_file_functions();
2631
2632    /* Load black list */
2633    accel_blacklist.entries = NULL;
2634    if (ZCG(enabled) && accel_startup_ok &&
2635        ZCG(accel_directives).user_blacklist_filename &&
2636        *ZCG(accel_directives.user_blacklist_filename)) {
2637        zend_accel_blacklist_init(&accel_blacklist);
2638        zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
2639    }
2640
2641    return SUCCESS;
2642}
2643
2644static void accel_free_ts_resources()
2645{
2646#ifndef ZTS
2647    accel_globals_dtor(&accel_globals);
2648#else
2649    ts_free_id(accel_globals_id);
2650#endif
2651}
2652
2653void accel_shutdown(void)
2654{
2655    zend_ini_entry *ini_entry;
2656    zend_bool file_cache_only = 0;
2657
2658    zend_accel_blacklist_shutdown(&accel_blacklist);
2659
2660    if (!ZCG(enabled) || !accel_startup_ok) {
2661        accel_free_ts_resources();
2662        return;
2663    }
2664
2665    if (ZCG(accel_directives).interned_strings_buffer) {
2666#ifndef ZTS
2667        zend_hash_clean(CG(auto_globals));
2668        zend_hash_clean(CG(function_table));
2669        zend_hash_clean(CG(class_table));
2670        zend_hash_clean(EG(zend_constants));
2671#endif
2672    }
2673
2674    accel_reset_pcre_cache();
2675
2676    zend_new_interned_string = orig_new_interned_string;
2677    zend_interned_strings_snapshot = orig_interned_strings_snapshot;
2678    zend_interned_strings_restore = orig_interned_strings_restore;
2679
2680#ifdef HAVE_OPCACHE_FILE_CACHE
2681    file_cache_only = ZCG(accel_directives).file_cache_only;
2682#endif
2683
2684    accel_free_ts_resources();
2685
2686    if (!file_cache_only) {
2687        zend_shared_alloc_shutdown();
2688    }
2689    zend_compile_file = accelerator_orig_compile_file;
2690
2691    if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
2692        ini_entry->on_modify = orig_include_path_on_modify;
2693    }
2694}
2695
2696void zend_accel_schedule_restart(zend_accel_restart_reason reason)
2697{
2698    if (ZCSG(restart_pending)) {
2699        /* don't schedule twice */
2700        return;
2701    }
2702    zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!");
2703
2704    SHM_UNPROTECT();
2705    ZCSG(restart_pending) = 1;
2706    ZCSG(restart_reason) = reason;
2707    ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
2708    ZCSG(accelerator_enabled) = 0;
2709
2710    if (ZCG(accel_directives).force_restart_timeout) {
2711        ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
2712    } else {
2713        ZCSG(force_restart_time) = 0;
2714    }
2715    SHM_PROTECT();
2716}
2717
2718/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
2719#ifdef ZEND_WIN32
2720#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub()
2721#else
2722#define accel_deactivate_now() accel_deactivate_sub()
2723#endif
2724
2725/* ensures it is OK to read SHM
2726    if it's not OK (restart in progress) returns FAILURE
2727    if OK returns SUCCESS
2728    MUST call accelerator_shm_read_unlock after done lock operations
2729*/
2730int accelerator_shm_read_lock(void)
2731{
2732    if (ZCG(counted)) {
2733        /* counted means we are holding read lock for SHM, so that nothing bad can happen */
2734        return SUCCESS;
2735    } else {
2736        /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
2737            or is in progress now */
2738        accel_activate_add(); /* acquire usage lock */
2739        /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
2740        if (ZCSG(restart_in_progress)) {
2741            /* we already were inside restart this means it's not safe to touch shm */
2742            accel_deactivate_now(); /* drop usage lock */
2743            return FAILURE;
2744        }
2745    }
2746    return SUCCESS;
2747}
2748
2749/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
2750void accelerator_shm_read_unlock(void)
2751{
2752    if (!ZCG(counted)) {
2753        /* counted is 0 - meaning we had to readlock manually, release readlock now */
2754        accel_deactivate_now();
2755    }
2756}
2757
2758ZEND_EXT_API zend_extension zend_extension_entry = {
2759    ACCELERATOR_PRODUCT_NAME,               /* name */
2760    ACCELERATOR_VERSION,                    /* version */
2761    "Zend Technologies",                    /* author */
2762    "http://www.zend.com/",                 /* URL */
2763    "Copyright (c) 1999-2015",              /* copyright */
2764    accel_startup,                          /* startup */
2765    NULL,                                   /* shutdown */
2766    accel_activate,                         /* per-script activation */
2767    accel_deactivate,                       /* per-script deactivation */
2768    NULL,                                   /* message handler */
2769    NULL,                                   /* op_array handler */
2770    NULL,                                   /* extended statement handler */
2771    NULL,                                   /* extended fcall begin handler */
2772    NULL,                                   /* extended fcall end handler */
2773    NULL,                                   /* op_array ctor */
2774    NULL,                                   /* op_array dtor */
2775    STANDARD_ZEND_EXTENSION_PROPERTIES
2776};
2777