root/tags/1.1.0/xcache.c

Revision 256, 62.2 kB (checked in by moo, 2 years ago)

trunk->1.1

  • merged [254] fix xcache.var_ttl displaying in info, fix xcache_get for ttl-unlimited
  • merged [250] admin: fix switcher class on active
  • merged [249] return 0 s instead of empty for deltatime
  • merged [201] coverager: avoid crash when upstream failed to compile file
Line 
1
2#undef DEBUG
3
4/* {{{ macros */
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8
9#include <signal.h>
10
11#include "php.h"
12#include "ext/standard/info.h"
13#include "ext/standard/md5.h"
14#include "ext/standard/php_math.h"
15#include "zend_extensions.h"
16#include "SAPI.h"
17
18#include "xcache.h"
19#include "optimizer.h"
20#include "coverager.h"
21#include "disassembler.h"
22#include "align.h"
23#include "stack.h"
24#include "xcache_globals.h"
25#include "processor.h"
26#include "utils.h"
27#include "const_string.h"
28#include "opcode_spec.h"
29
30#ifdef DEBUG
31#   undef NDEBUG
32#   undef inline
33#   define inline
34#else
35#   ifndef NDEBUG
36#       define NDEBUG
37#   endif
38#endif
39#include <assert.h>
40
41#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > pentry->ctime + (pentry)->ttl)
42#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
43#define LOCK(x) xc_lock(x->lck)
44#define UNLOCK(x) xc_unlock(x->lck)
45
46#define ENTER_LOCK_EX(x) \
47    xc_lock(x->lck); \
48    zend_try { \
49        do
50#define LEAVE_LOCK_EX(x) \
51        while (0); \
52    } zend_catch { \
53        catched = 1; \
54    } zend_end_try(); \
55    xc_unlock(x->lck)
56
57#define ENTER_LOCK(x) do { \
58    int catched = 0; \
59    ENTER_LOCK_EX(x)
60#define LEAVE_LOCK(x) \
61    LEAVE_LOCK_EX(x); \
62    if (catched) { \
63        zend_bailout(); \
64    } \
65} while(0)
66
67/* }}} */
68
69/* {{{ globals */
70static char *xc_shm_scheme = NULL;
71static char *xc_mmap_path = NULL;
72static char *xc_coredump_dir = NULL;
73
74static xc_hash_t xc_php_hcache = {0};
75static xc_hash_t xc_php_hentry = {0};
76static xc_hash_t xc_var_hcache = {0};
77static xc_hash_t xc_var_hentry = {0};
78
79static zend_ulong xc_php_ttl    = 0;
80static zend_ulong xc_var_maxttl = 0;
81
82enum { xc_deletes_gc_interval = 120 };
83static zend_ulong xc_php_gc_interval = 0;
84static zend_ulong xc_var_gc_interval = 0;
85
86/* total size */
87static zend_ulong xc_php_size  = 0;
88static zend_ulong xc_var_size  = 0;
89
90static xc_cache_t **xc_php_caches = NULL;
91static xc_cache_t **xc_var_caches = NULL;
92
93static zend_bool xc_initized = 0;
94static zend_compile_file_t *origin_compile_file;
95
96static zend_bool xc_test = 0;
97static zend_bool xc_readonly_protection = 0;
98
99static zend_bool xc_module_gotup = 0;
100static zend_bool xc_zend_extension_gotup = 0;
101#if !COMPILE_DL_XCACHE
102#   define zend_extension_entry xcache_zend_extension_entry
103#endif
104ZEND_DLEXPORT zend_extension zend_extension_entry;
105ZEND_DECLARE_MODULE_GLOBALS(xcache);
106/* }}} */
107
108/* any function in *_dmz is only safe be called within locked(single thread) area */
109
110static inline int xc_entry_equal_dmz(xc_entry_t *a, xc_entry_t *b) /* {{{ */
111{
112    /* this function isn't required but can be in dmz */
113
114    if (a->type != b->type) {
115        return 0;
116    }
117    switch (a->type) {
118        case XC_TYPE_PHP:
119#ifdef HAVE_INODE
120            do {
121                xc_entry_data_php_t *ap = a->data.php;
122                xc_entry_data_php_t *bp = b->data.php;
123                return ap->inode == bp->inode
124                    && ap->device == bp->device;
125            } while(0);
126#endif
127            /* fall */
128
129        case XC_TYPE_VAR:
130            do {
131#ifdef IS_UNICODE
132                if (a->name_type == IS_UNICODE) {
133                    if (a->name.ustr.len != b->name.ustr.len) {
134                        return 0;
135                    }
136                    return memcmp(a->name.ustr.val, b->name.ustr.val, (a->name.ustr.len + 1) * sizeof(UChar)) == 0;
137                }
138                else {
139                    return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
140                }
141#else
142                return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
143#endif
144
145            } while(0);
146        default:
147            assert(0);
148    }
149    return 0;
150}
151/* }}} */
152static void xc_entry_free_real_dmz(volatile xc_entry_t *xce) /* {{{ */
153{
154    xce->cache->mem->handlers->free(xce->cache->mem, (xc_entry_t *)xce);
155}
156/* }}} */
157static void xc_entry_add_dmz(xc_entry_t *xce) /* {{{ */
158{
159    xc_entry_t **head = &(xce->cache->entries[xce->hvalue]);
160    xce->next = *head;
161    *head = xce;
162    xce->cache->entries_count ++;
163}
164/* }}} */
165static xc_entry_t *xc_entry_store_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
166{
167    xc_entry_t *stored_xce;
168
169    xce->hits  = 0;
170    xce->ctime = XG(request_time);
171    xce->atime = XG(request_time);
172    stored_xce = xc_processor_store_xc_entry_t(xce TSRMLS_CC);
173    if (stored_xce) {
174        xc_entry_add_dmz(stored_xce);
175        return stored_xce;
176    }
177    else {
178        xce->cache->ooms ++;
179        return NULL;
180    }
181}
182/* }}} */
183static void xc_entry_free_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
184{
185    xce->cache->entries_count --;
186    if (xce->refcount == 0) {
187        xc_entry_free_real_dmz(xce);
188    }
189    else {
190        xce->next = xce->cache->deletes;
191        xce->cache->deletes = xce;
192        xce->dtime = XG(request_time);
193        xce->cache->deletes_count ++;
194    }
195    return;
196}
197/* }}} */
198static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
199{
200    xc_entry_t **pp = &(xce->cache->entries[xce->hvalue]);
201    xc_entry_t *p;
202    for (p = *pp; p; pp = &(p->next), p = p->next) {
203        if (xc_entry_equal_dmz(xce, p)) {
204            /* unlink */
205            *pp = p->next;
206            xc_entry_free_dmz(xce TSRMLS_CC);
207            return;
208        }
209    }
210    assert(0);
211}
212/* }}} */
213static xc_entry_t *xc_entry_find_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
214{
215    xc_entry_t *p;
216    for (p = xce->cache->entries[xce->hvalue]; p; p = p->next) {
217        if (xc_entry_equal_dmz(xce, p)) {
218            if (p->type == XC_TYPE_VAR || /* PHP */ p->data.php->mtime == xce->data.php->mtime) {
219                p->hits ++;
220                p->atime = XG(request_time);
221                return p;
222            }
223            else {
224                xc_entry_remove_dmz(p TSRMLS_CC);
225                return NULL;
226            }
227        }
228    }
229    return NULL;
230}
231/* }}} */
232static void xc_entry_hold_php_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
233{
234#ifdef DEBUG
235    fprintf(stderr, "hold %s\n", ZSTR_S(xce->name));
236#endif
237    xce->refcount ++;
238    xc_stack_push(&XG(php_holds)[xce->cache->cacheid], (void *)xce);
239}
240/* }}} */
241#if 0
242static void xc_entry_hold_var_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
243{
244    xce->refcount ++;
245    xc_stack_push(&XG(var_holds)[xce->cache->cacheid], (void *)xce);
246}
247/* }}} */
248#endif
249
250/* helper function that loop through each entry */
251#define XC_ENTRY_APPLY_FUNC(name) int name(xc_entry_t *entry TSRMLS_DC)
252typedef XC_ENTRY_APPLY_FUNC((*cache_apply_dmz_func_t));
253static void xc_entry_apply_dmz(xc_cache_t *cache, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
254{
255    xc_entry_t *p, **pp;
256    int i, c;
257
258    for (i = 0, c = cache->hentry->size; i < c; i ++) {
259        pp = &(cache->entries[i]);
260        for (p = *pp; p; p = *pp) {
261            if (apply_func(p TSRMLS_CC)) {
262                /* unlink */
263                *pp = p->next;
264                xc_entry_free_dmz(p TSRMLS_CC);
265            }
266            else {
267                pp = &(p->next);
268            }
269        }
270    }
271}
272/* }}} */
273
274#define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
275/* call graph:
276 * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_php_entry_dmz
277 * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_var_entry_dmz
278 */
279static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_dmz) /* {{{ */
280{
281#ifdef DEBUG
282    fprintf(stderr, "ttl %d, %d %d\n", XG(request_time), entry->atime, xc_php_ttl);
283#endif
284    if (XG(request_time) > entry->atime + xc_php_ttl) {
285        return 1;
286    }
287    return 0;
288}
289/* }}} */
290static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_dmz) /* {{{ */
291{
292    if (VAR_ENTRY_EXPIRED(entry)) {
293        return 1;
294    }
295    return 0;
296}
297/* }}} */
298static void xc_gc_expires_one(xc_cache_t *cache, zend_ulong gc_interval, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
299{
300#ifdef DEBUG
301    fprintf(stderr, "interval %d, %d %d\n", XG(request_time), cache->last_gc_expires, gc_interval);
302#endif
303    if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
304        ENTER_LOCK(cache) {
305            if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
306                cache->last_gc_expires = XG(request_time);
307                xc_entry_apply_dmz(cache, apply_func TSRMLS_CC);
308            }
309        } LEAVE_LOCK(cache);
310    }
311}
312/* }}} */
313static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
314{
315    int i, c;
316
317    if (!xc_php_ttl || !xc_php_gc_interval) {
318        return;
319    }
320
321    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
322        xc_gc_expires_one(xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_dmz TSRMLS_CC);
323    }
324}
325/* }}} */
326static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
327{
328    int i, c;
329
330    if (!xc_var_gc_interval) {
331        return;
332    }
333
334    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
335        xc_gc_expires_one(xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_dmz TSRMLS_CC);
336    }
337}
338/* }}} */
339
340static XC_CACHE_APPLY_FUNC(xc_gc_delete_dmz) /* {{{ */
341{
342    xc_entry_t *p, **pp;
343
344    pp = &cache->deletes;
345    for (p = *pp; p; p = *pp) {
346        if (XG(request_time) - p->dtime > 3600) {
347            p->refcount = 0;
348            /* issue warning here */
349        }
350        if (p->refcount == 0) {
351            /* unlink */
352            *pp = p->next;
353            cache->deletes_count --;
354            xc_entry_free_real_dmz(p);
355        }
356        else {
357            pp = &(p->next);
358        }
359    }
360}
361/* }}} */
362static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
363{
364    if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
365        ENTER_LOCK(cache) {
366            if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
367                cache->last_gc_deletes = XG(request_time);
368                xc_gc_delete_dmz(cache TSRMLS_CC);
369            }
370        } LEAVE_LOCK(cache);
371    }
372}
373/* }}} */
374static void xc_gc_deletes(TSRMLS_D) /* {{{ */
375{
376    int i, c;
377
378    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
379        xc_gc_deletes_one(xc_php_caches[i] TSRMLS_CC);
380    }
381
382    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
383        xc_gc_deletes_one(xc_var_caches[i] TSRMLS_CC);
384    }
385}
386/* }}} */
387
388/* helper functions for user functions */
389static void xc_fillinfo_dmz(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
390{
391    zval *blocks;
392    const xc_block_t *b;
393#ifndef NDEBUG
394    xc_memsize_t avail = 0;
395#endif
396    xc_mem_t *mem = cache->mem;
397    const xc_mem_handlers_t *handlers = mem->handlers;
398    zend_ulong interval = (cachetype == XC_TYPE_PHP) ? xc_php_gc_interval : xc_var_gc_interval;
399
400    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
401    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
402    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cache->misses);
403    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cache->hits);
404    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cache->clogs);
405    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cache->ooms);
406
407    add_assoc_long_ex(return_value, ZEND_STRS("cached"),    cache->entries_count);
408    add_assoc_long_ex(return_value, ZEND_STRS("deleted"),   cache->deletes_count);
409    if (interval) {
410        add_assoc_long_ex(return_value, ZEND_STRS("gc"),    (cache->last_gc_expires + interval) - XG(request_time));
411    }
412    else {
413        add_assoc_null_ex(return_value, ZEND_STRS("gc"));
414    }
415
416    MAKE_STD_ZVAL(blocks);
417    array_init(blocks);
418
419    add_assoc_long_ex(return_value, ZEND_STRS("size"),  handlers->size(mem));
420    add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
421    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
422
423    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
424        zval *bi;
425
426        MAKE_STD_ZVAL(bi);
427        array_init(bi);
428
429        add_assoc_long_ex(bi, ZEND_STRS("size"),   handlers->block_size(b));
430        add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
431        add_next_index_zval(blocks, bi);
432#ifndef NDEBUG
433        avail += handlers->block_size(b);
434#endif
435    }
436    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
437    assert(avail == handlers->avail(mem));
438}
439/* }}} */
440static void xc_fillentry_dmz(xc_entry_t *entry, int del, zval *list TSRMLS_DC) /* {{{ */
441{
442    zval* ei;
443    xc_entry_data_php_t *php;
444    xc_entry_data_var_t *var;
445
446    ALLOC_INIT_ZVAL(ei);
447    array_init(ei);
448
449    add_assoc_long_ex(ei, ZEND_STRS("size"),     entry->size);
450    add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry->refcount);
451    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
452    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
453    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
454    if (del) {
455        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
456    }
457#ifdef IS_UNICODE
458    do {
459        zval *zv;
460        ALLOC_INIT_ZVAL(zv);
461        switch (entry->name_type) {
462            case IS_UNICODE:
463                    ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
464                break;
465            case IS_STRING:
466                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
467                break;
468            default:
469                assert(0);
470        }
471        zv->type = entry->name_type;
472        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
473    } while (0);
474#else
475    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
476#endif
477    switch (entry->type) {
478        case XC_TYPE_PHP:
479            php = entry->data.php;
480            add_assoc_long_ex(ei, ZEND_STRS("sourcesize"),    php->sourcesize);
481#ifdef HAVE_INODE
482            add_assoc_long_ex(ei, ZEND_STRS("device"),        php->device);
483            add_assoc_long_ex(ei, ZEND_STRS("inode"),         php->inode);
484#endif
485            add_assoc_long_ex(ei, ZEND_STRS("mtime"),         php->mtime);
486
487#ifdef HAVE_XCACHE_CONSTANT
488            add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
489#endif
490            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"),  php->funcinfo_cnt);
491            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),     php->classinfo_cnt);
492            break;
493        case XC_TYPE_VAR:
494            var = entry->data.var;
495            break;
496
497        default:
498            assert(0);
499    }
500
501    add_next_index_zval(list, ei);
502}
503/* }}} */
504static void xc_filllist_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
505{
506    zval* list;
507    int i, c;
508    xc_entry_t *e;
509
510    ALLOC_INIT_ZVAL(list);
511    array_init(list);
512
513    for (i = 0, c = cache->hentry->size; i < c; i ++) {
514        for (e = cache->entries[i]; e; e = e->next) {
515            xc_fillentry_dmz(e, 0, list TSRMLS_CC);
516        }
517    }
518    add_assoc_zval(return_value, "cache_list", list);
519
520    ALLOC_INIT_ZVAL(list);
521    array_init(list);
522    for (e = cache->deletes; e; e = e->next) {
523        xc_fillentry_dmz(e, 1, list TSRMLS_CC);
524    }
525    add_assoc_zval(return_value, "deleted_list", list);
526}
527/* }}} */
528
529static zend_op_array *xc_entry_install(xc_entry_t *xce, zend_file_handle *h TSRMLS_DC) /* {{{ */
530{
531    zend_uint i;
532    xc_entry_data_php_t *p = xce->data.php;
533#ifndef ZEND_ENGINE_2
534    /* new ptr which is stored inside CG(class_table) */
535    xc_cest_t **new_cest_ptrs = (xc_cest_t **)do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt);
536#endif
537
538#ifdef HAVE_XCACHE_CONSTANT
539    /* install constant */
540    for (i = 0; i < p->constinfo_cnt; i ++) {
541        xc_constinfo_t *ci = &p->constinfos[i];
542        xc_install_constant(xce->name.str.val, &ci->constant,
543                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
544    }
545#endif
546
547    /* install function */
548    for (i = 0; i < p->funcinfo_cnt; i ++) {
549        xc_funcinfo_t  *fi = &p->funcinfos[i];
550        xc_install_function(xce->name.str.val, &fi->func,
551                UNISW(0, fi->type), fi->key, fi->key_size TSRMLS_CC);
552    }
553
554    /* install class */
555    for (i = 0; i < p->classinfo_cnt; i ++) {
556        xc_classinfo_t *ci = &p->classinfos[i];
557#ifndef ZEND_ENGINE_2
558        zend_class_entry *ce = CestToCePtr(ci->cest);
559        /* fix pointer to the be which inside class_table */
560        if (ce->parent) {
561            zend_uint class_idx = (/* class_num */ (int) ce->parent) - 1;
562            assert(class_idx < i);
563            ci->cest.parent = new_cest_ptrs[class_idx];
564        }
565        new_cest_ptrs[i] =
566#endif
567        xc_install_class(xce->name.str.val, &ci->cest,
568                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
569    }
570
571    i = 1;
572    zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
573    if (h) {
574        zend_llist_add_element(&CG(open_files), h);
575    }
576
577#ifndef ZEND_ENGINE_2
578    free_alloca(new_cest_ptrs);
579#endif
580    return p->op_array;
581}
582/* }}} */
583
584static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
585{
586    int i;
587    xc_stack_t *s;
588    xc_cache_t *cache;
589    xc_entry_t *xce;
590
591    for (i = 0; i < cachecount; i ++) {
592        s = &holds[i];
593#ifdef DEBUG
594        fprintf(stderr, "holded %d\n", xc_stack_size(s));
595#endif
596        if (xc_stack_size(s)) {
597            cache = ((xc_entry_t *)xc_stack_top(s))->cache;
598            ENTER_LOCK(cache) {
599                while (xc_stack_size(s)) {
600                    xce = (xc_entry_t*) xc_stack_pop(s);
601#ifdef DEBUG
602                    fprintf(stderr, "unhold %s\n", ZSTR_S(xce->name));
603#endif
604                    xce->refcount --;
605                    assert(xce->refcount >= 0);
606                }
607            } LEAVE_LOCK(cache);
608        }
609    }
610}
611/* }}} */
612static void xc_entry_unholds(TSRMLS_D) /* {{{ */
613{
614    xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
615    xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
616}
617/* }}} */
618static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf TSRMLS_DC) /* {{{ */
619{
620    char filepath[1024];
621    char *paths, *path;
622    char *tokbuf;
623    int size = strlen(include_path) + 1;
624    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
625
626    paths = (char *)do_alloca(size);
627    memcpy(paths, include_path, size);
628
629    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
630        if (strlen(path) + strlen(filename) + 1 > 1024) {
631            continue;
632        }
633        snprintf(filepath, sizeof(filepath), "%s/%s", path, filename);
634        if (VCWD_STAT(filepath, pbuf) == 0) {
635            free_alloca(paths);
636            return 0;
637        }
638    }
639
640    free_alloca(paths);
641
642    return 1;
643}
644/* }}} */
645
646#define HASH(i) (i)
647#define HASH_USTR_L(t, s, l) HASH(zend_u_inline_hash_func(t, s, (l + 1) * sizeof(UChar)))
648#define HASH_STR_L(s, l) HASH(zend_inline_hash_func(s, l + 1))
649#define HASH_STR(s) HASH_STR_L(s, strlen(s) + 1)
650#define HASH_NUM(n) HASH(n)
651static inline xc_hash_value_t xc_entry_hash_var(xc_entry_t *xce TSRMLS_DC) /* {{{ */
652{
653    return UNISW(NOTHING, UG(unicode) ? HASH_USTR_L(xce->name_type, xce->name.uni.val, xce->name.uni.len) :)
654        HASH_STR_L(xce->name.str.val, xce->name.str.len);
655}
656/* }}} */
657static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce TSRMLS_DC) /* {{{ */
658{
659#ifdef HAVE_INODE
660    return HASH(xce->data.php->device + xce->data.php->inode);
661#else
662    return xc_entry_hash_var(xce TSRMLS_CC);
663#endif
664}
665/* }}} */
666static int xc_entry_init_key_php(xc_entry_t *xce, char *filename TSRMLS_DC) /* {{{ */
667{
668    struct stat buf, *pbuf;
669    xc_hash_value_t hv;
670    int cacheid;
671    xc_entry_data_php_t *php;
672    char *ptr;
673
674    if (!filename || !SG(request_info).path_translated) {
675        return 0;
676    }
677
678    do {
679        if (strcmp(SG(request_info).path_translated, filename) == 0) {
680            /* sapi has already done this stat() for us */
681            pbuf = sapi_get_stat(TSRMLS_C);
682            if (pbuf) {
683                break;
684            }
685        }
686
687        /* absolute path */
688        pbuf = &buf;
689        if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
690            if (VCWD_STAT(filename, pbuf) != 0) {
691                return 0;
692            }
693            break;
694        }
695
696        /* relative path */
697        if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
698            ptr = filename + 1;
699            if (*ptr == '.') {
700                while (*(++ptr) == '.');
701                if (!IS_SLASH(*ptr)) {
702                    goto not_relative_path;
703                }   
704            }
705
706            if (VCWD_STAT(filename, pbuf) != 0) {
707                return 0;
708            }
709            break;
710        }
711not_relative_path:
712
713        /* use include_path */
714        if (xc_stat(filename, PG(include_path), pbuf TSRMLS_CC) != 0) {   
715            return 0;
716        }
717    } while (0);
718
719    if (XG(request_time) - pbuf->st_mtime < 2) {
720        return 0;
721    }
722
723    UNISW(NOTHING, xce->name_type = IS_STRING;)
724    xce->name.str.val = filename;
725    xce->name.str.len = strlen(filename);
726
727    php = xce->data.php;
728    php->mtime        = pbuf->st_mtime;
729#ifdef HAVE_INODE
730    php->device       = pbuf