root/eaccelerator/tags/0.9.4-rc1/eaccelerator.c

Revision 134, 86.4 kB (checked in by zoeloelip, 3 years ago)

* Removed executor hooks, they weren't used anyway.
* Check if eA has a valid cache directory.
* Make the init of php fail if the initialisation of the shared memory

or cache directory failes.

* set shared memory size in eaccelerator.ini to 0 so the default OS

size is used.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2    +----------------------------------------------------------------------+
3    | eAccelerator project                                                 |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2004 - 2005 eAccelerator                               |
6    | http://eaccelerator.net                                                      |
7    +----------------------------------------------------------------------+
8    | This program is free software; you can redistribute it and/or        |
9    | modify it under the terms of the GNU General Public License          |
10    | as published by the Free Software Foundation; either version 2       |
11    | of the License, or (at your option) any later version.               |
12    |                                                                      |
13    | This program is distributed in the hope that it will be useful,      |
14    | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
15    | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
16    | GNU General Public License for more details.                         |
17    |                                                                      |
18    | You should have received a copy of the GNU General Public License    |
19    | along with this program; if not, write to the Free Software          |
20    | Foundation, Inc., 59 Temple Place - Suite 330, Boston,               |
21    | MA  02111-1307, USA.                                                 |
22    |                                                                      |
23    | A copy is availble at http://www.gnu.org/copyleft/gpl.txt            |
24    +----------------------------------------------------------------------+
25    | Author(s): Dmitry Stogov <dstogov@users.sourceforge.net>             |
26    |            Seung Woo <segv@sayclub.com>                              |
27    |            Everaldo Canuto <everaldo_canuto@yahoo.com.br>            |
28    +----------------------------------------------------------------------+
29    $Id$
30 */
31
32 #include "eaccelerator.h"
33 #include "eaccelerator_version.h"
34
35 #ifdef HAVE_EACCELERATOR
36
37 #include "opcodes.h"
38
39 #include "zend.h"
40 #include "zend_API.h"
41 #include "zend_extensions.h"
42
43 #include "webui.h"
44 #include "debug.h"
45 #include "shm.h"
46 #include "session.h"
47 #include "content.h"
48 #include "cache.h"
49 #include "ea_store.h"
50 #include "ea_restore.h"
51
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #ifdef ZEND_WIN32
55 #  include "win32/time.h"
56 #  include <time.h>
57 #  include <sys/utime.h>
58 #else
59 #  include <sys/file.h>
60 #  include <sys/time.h>
61 #  include <utime.h>
62 #endif
63 #include <fcntl.h>
64
65 #ifndef O_BINARY
66 #  define O_BINARY 0
67 #endif
68
69 #include "php.h"
70 #include "php_ini.h"
71 #include "php_logos.h"
72 #include "main/fopen_wrappers.h"
73 #include "ext/standard/info.h"
74 #include "ext/standard/php_incomplete_class.h"
75 #include "ext/standard/md5.h"
76
77 #include "SAPI.h"
78
79 #define MAX_DUP_STR_LEN 256
80
81 /* Globals (different for each process/thread) */
82 ZEND_DECLARE_MODULE_GLOBALS(eaccelerator)
83
84 /* Globals (common for each process/thread) */
85 static long eaccelerator_shm_size = 0;
86 long eaccelerator_shm_max = 0;
87 static long eaccelerator_shm_ttl = 0;
88 static long eaccelerator_shm_prune_period = 0;
89 extern long eaccelerator_debug;
90 static zend_bool eaccelerator_check_mtime = 1;
91 zend_bool eaccelerator_scripts_shm_only = 0;
92
93 eaccelerator_mm* eaccelerator_mm_instance = NULL;
94 static int eaccelerator_is_zend_extension = 0;
95 static int eaccelerator_is_extension      = 0;
96 zend_extension* ZendOptimizer = NULL;
97
98 static HashTable eaccelerator_global_function_table;
99 static HashTable eaccelerator_global_class_table;
100
101 int binary_eaccelerator_version;
102 int binary_php_version;
103 int binary_zend_version;
104
105 #ifdef ZEND_ENGINE_2
106 /* pointer to the properties_info hashtable destructor */
107 extern dtor_func_t properties_info_dtor;
108 #endif
109
110 /* saved original functions */
111 static zend_op_array *(*mm_saved_zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
112
113 /* external declarations */
114 PHPAPI void php_stripslashes(char *str, int *len TSRMLS_DC);
115
116 ZEND_DLEXPORT zend_op_array* eaccelerator_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC);
117
118 /******************************************************************************/
119 /* hash mm functions                                                          */
120 /******************************************************************************/
121
122 inline unsigned int hash_mm(const char *data, int len) {
123   unsigned int h;
124   const char *e = data + len;
125   for (h = 2166136261U; data < e; ) {
126     h *= 16777619;
127     h ^= *data++;
128   }
129   return h;
130 }
131
132 /* Find a script entry with the given hash key */
133 static mm_cache_entry* hash_find_mm(const char  *key,
134                                     struct stat *buf,
135                                     int         *nreloads,
136                                     time_t      ttl) {
137   unsigned int hv, slot;
138   mm_cache_entry *p, *q;
139
140 #ifdef EACCELERATOR_USE_INODE
141   hv = buf->st_dev + buf->st_ino;
142 #else
143   hv = hash_mm(key, strlen(key));
144 #endif
145   slot = hv & MM_HASH_MAX;
146
147   EACCELERATOR_LOCK_RW();
148   q = NULL;
149   p = eaccelerator_mm_instance->hash[slot];
150   while (p != NULL) {
151 #ifdef EACCELERATOR_USE_INODE
152     if (p->st_dev == buf->st_dev && p->st_ino == buf->st_ino) {
153       struct stat buf2;
154       if ((eaccelerator_check_mtime &&
155           (buf->st_mtime != p->mtime || buf->st_size != p->filesize)) ||
156           (strcmp(p->realfilename, key) != 0 &&
157            (stat(p->realfilename,&buf2) != 0 ||
158            buf2.st_dev != buf->st_dev ||
159            buf2.st_ino != buf->st_ino))) {
160 #else
161     if ((p->hv == hv) && (strcmp(p->realfilename, key) == 0)) {
162       if (eaccelerator_check_mtime &&
163           (buf->st_mtime != p->mtime || buf->st_size != p->filesize)) {
164 #endif
165         /* key is invalid. Remove it. */
166         *nreloads = p->nreloads+1;
167         if (q == NULL) {
168           eaccelerator_mm_instance->hash[slot] = p->next;
169         } else {
170           q->next = p->next;
171         }
172         eaccelerator_mm_instance->hash_cnt--;
173         if (p->use_cnt > 0) {
174           /* key is used by other process/thred. Shedule it to remove */
175           p->removed = 1;
176           p->next = eaccelerator_mm_instance->removed;
177           eaccelerator_mm_instance->removed = p;
178           eaccelerator_mm_instance->rem_cnt++;
179           EACCELERATOR_UNLOCK_RW();
180           return NULL;
181         } else {
182           /* key is unused. Remove it. */
183           eaccelerator_free_nolock(p);
184           EACCELERATOR_UNLOCK_RW();
185           return NULL;
186         }
187       } else {
188         /* key is valid */
189         p->nhits++;
190         p->use_cnt++;
191         p->ttl = ttl;
192         EACCELERATOR_UNLOCK_RW();
193         return p;
194       }
195     }
196     q = p;
197     p = p->next;
198   }
199   EACCELERATOR_UNLOCK_RW();
200   return NULL;
201 }
202
203 /* Add a new entry to the hashtable */
204 static void hash_add_mm(mm_cache_entry *x) {
205   mm_cache_entry *p,*q;
206   unsigned int slot;
207 #ifdef EACCELERATOR_USE_INODE
208   slot = (x->st_dev + x->st_ino) & MM_HASH_MAX;
209 #else
210   x->hv = hash_mm(x->realfilename, strlen(x->realfilename));
211   slot = x->hv & MM_HASH_MAX;
212 #endif
213
214   EACCELERATOR_LOCK_RW();
215   x->next = eaccelerator_mm_instance->hash[slot];
216   eaccelerator_mm_instance->hash[slot] = x;
217   eaccelerator_mm_instance->hash_cnt++;
218   q = x;
219   p = x->next;
220   while (p != NULL) {
221 #ifdef EACCELERATOR_USE_INODE
222     if ((p->st_dev == x->st_dev) && (p->st_ino == x->st_ino)) {
223 #else
224     if ((p->hv == x->hv) &&
225         (strcmp(p->realfilename, x->realfilename) == 0)) {
226 #endif
227       q->next = p->next;
228       eaccelerator_mm_instance->hash_cnt--;
229       eaccelerator_mm_instance->hash[slot]->nreloads += p->nreloads;
230       if (p->use_cnt > 0) {
231         /* key is used by other process/thred. Shedule it to remove */
232         p->removed = 1;
233         p->next = eaccelerator_mm_instance->removed;
234         eaccelerator_mm_instance->removed = p;
235         eaccelerator_mm_instance->rem_cnt++;
236         EACCELERATOR_UNLOCK_RW();
237         return;
238       } else {
239         /* key is unused. Remove it. */
240         eaccelerator_free_nolock(p);
241         EACCELERATOR_UNLOCK_RW();
242         return;
243       }
244     }
245     q = p;
246     p = p->next;
247   }
248   EACCELERATOR_UNLOCK_RW();
249 }
250
251 /* check the cache dir */
252 static int check_cache_dir(char *cache_dir) {
253   struct stat buf;
254   int uid = 0;
255   int gid = 0;
256
257   if (stat(cache_dir, &buf) == -1) {
258     ea_debug_error("Cache dir does not exist (could not stat %s)\n", cache_dir);
259     return 0;
260   }
261   if (!(buf.st_mode & S_IFDIR)) {
262     ea_debug_error("%s is not a directory!\n", cache_dir);
263     return 0;
264   }
265 #if 0
266   uid = getuid();
267   gid = getgid();
268   if (!((buf.st_uid == uid && (buf.st_mode & (S_IRUSR | S_IWUSR))) /* not root, owner and rw */
269         || (buf.st_gid == gid && (buf.st_mode & (S_IRGRP | S_IWGRP))) /* not root, group and rw */
270         || (buf.st_mode & (S_IROTH & S_IWOTH)))) { /* other and rw */
271     ea_debug_error("%s hasn't got the right permissions!\n", cache_dir);
272     return 0;
273   }
274 #endif
275   return 1;
276 }
277
278 /* Initialise the shared memory */
279 static int init_mm(TSRMLS_D) {
280   pid_t  owner = getpid();
281   MM     *mm;
282   size_t total;
283   char   mm_path[MAXPATHLEN];
284
285 /*  if (getppid() != 1) return SUCCESS; */ /*???*/
286 #ifdef ZEND_WIN32
287     snprintf(mm_path, MAXPATHLEN, "%s.%s", EACCELERATOR_MM_FILE, sapi_module.name);
288 #else
289     snprintf(mm_path, MAXPATHLEN, "%s.%s%d", EACCELERATOR_MM_FILE, sapi_module.name, getpid());
290 #endif
291 /*  snprintf(mm_path, MAXPATHLEN, "%s.%s%d", EACCELERATOR_MM_FILE, sapi_module.name, geteuid());*/
292   if ((eaccelerator_mm_instance = (eaccelerator_mm*)mm_attach(eaccelerator_shm_size*1024*1024, mm_path)) != NULL) {
293 #ifdef ZTS
294     mm_mutex = tsrm_mutex_alloc();
295 #endif
296     return SUCCESS;
297   }
298   mm = mm_create(eaccelerator_shm_size*1024*1024, mm_path);
299   if (!mm) {
300     return FAILURE;
301   }
302 #ifdef ZEND_WIN32
303   ea_debug_printf(EA_DEBUG, "init_mm [%d]\n", getpid());
304 #else
305   ea_debug_printf(EA_DEBUG, "init_mm [%d,%d]\n", getpid(), getppid());
306 #endif
307 #ifdef ZTS
308   mm_mutex = tsrm_mutex_alloc();
309 #endif
310   total = mm_available(mm);
311   eaccelerator_mm_instance = mm_malloc_lock(mm, sizeof(*eaccelerator_mm_instance));
312   if (!eaccelerator_mm_instance) {
313     return FAILURE;
314   }
315   mm_set_attach(mm, eaccelerator_mm_instance);
316   memset(eaccelerator_mm_instance, 0, sizeof(*eaccelerator_mm_instance));
317   eaccelerator_mm_instance->owner = owner;
318   eaccelerator_mm_instance->mm    = mm;
319   eaccelerator_mm_instance->total = total;
320   eaccelerator_mm_instance->hash_cnt = 0;
321   eaccelerator_mm_instance->rem_cnt  = 0;
322   eaccelerator_mm_instance->enabled = 1;
323   eaccelerator_mm_instance->optimizer_enabled = 1;
324   eaccelerator_mm_instance->removed = NULL;
325   eaccelerator_mm_instance->locks = NULL;
326   eaccelerator_mm_instance->user_hash_cnt = 0;
327   eaccelerator_mm_instance->last_prune = time(0);
328   EACCELERATOR_PROTECT();
329
330   if (!check_cache_dir(EAG(cache_dir))) {
331     return FAILURE;
332   }
333  
334   return SUCCESS;
335 }
336
337 /* Clean up the shared memory */
338 static void shutdown_mm(TSRMLS_D) {
339   if (eaccelerator_mm_instance) {
340 #ifdef ZEND_WIN32
341     if (eaccelerator_mm_instance->owner == getpid()) {
342 #else
343     if (getpgrp() == getpid()) {
344 #endif
345       MM *mm = eaccelerator_mm_instance->mm;
346 #ifdef ZEND_WIN32
347       ea_debug_printf(EA_DEBUG, "shutdown_mm [%d]\n", getpid());
348 #else
349       ea_debug_printf(EA_DEBUG, "shutdown_mm [%d,%d]\n", getpid(), getppid());
350 #endif
351 #ifdef ZTS
352       tsrm_mutex_free(mm_mutex);
353 #endif
354       if (mm) {
355         mm_destroy(mm);
356       }
357       eaccelerator_mm_instance = NULL;
358     }
359   }
360 }
361
362
363 static int encode_version(const char *s) {
364   unsigned int v1 = 0;
365   unsigned int v2 = 0;
366   unsigned int v3 = 0;
367   unsigned int c;
368   char m = '.';
369   sscanf(s, "%u.%u%c%u",&v1,&v2,&m,&v3);
370   switch (m) {
371     case  'a': c = 0; break;
372     case  'b': c = 1; break;
373     case  '.': c = 2; break;
374     case  's': c = 15; break;
375     default: c = 2;
376   }
377   return ((v1 & 0xf) << 20) |
378          ((v2 & 0xff) << 12) |
379          ((c & 0xf) << 8) |
380          (v3 & 0xff);
381 }
382
383 static void decode_version(char *version, int v) {
384   int t = (v & 0x000f00) >> 8;
385   char c;
386   switch (t) {
387     case  0: c = 'a'; break;
388     case  1: c = 'b'; break;
389     case  2: c = '.'; break;
390     case 15: c = 's'; break;
391     default: c = '.';
392   }
393   snprintf(version, 16, "%d.%d%c%d", (v & 0xf00000) >> 20,
394                                      (v & 0x0ff000) >> 12,
395                                      c,
396                                      (v & 0x0000ff));
397 }
398
399 #ifdef EACCELERATOR_USE_INODE
400 static int eaccelerator_inode_key(char* s, dev_t dev, ino_t ino TSRMLS_DC) {
401   int n;
402   strncpy(s, EAG(cache_dir), MAXPATHLEN-1);
403   strlcat(s, "/eaccelerator-", MAXPATHLEN-1);
404   n = strlen(s);
405   while (dev > 0) {
406     if (n >= MAXPATHLEN) return 0;
407     s[n++] = (dev % 10) +'0';
408     dev /= 10;
409   }
410   if (n >= MAXPATHLEN) return 0;
411   s[n++] = '.';
412   while (ino > 0) {
413     if (n >= MAXPATHLEN) return 0;
414     s[n++] = (ino % 10) +'0';
415     ino /= 10;
416   }
417   if (n >= MAXPATHLEN) return 0;
418   s[n++] = '\000';
419   return 1;
420 }
421 #endif
422
423 /* Function to create a hash key when filenames are used */
424 int eaccelerator_md5(char* s, const char* prefix, const char* key TSRMLS_DC) {
425 #if defined(PHP_MAJOR_VERSION) && defined(PHP_MINOR_VERSION) && \
426     ((PHP_MAJOR_VERSION > 4) || (PHP_MAJOR_VERSION == 4 && PHP_MINOR_VERSION > 1))
427   char md5str[33];
428   PHP_MD5_CTX context;
429   unsigned char digest[16];
430
431   md5str[0] = '\0';
432   PHP_MD5Init(&context);
433   PHP_MD5Update(&context, (unsigned char*)key, strlen(key));
434   PHP_MD5Final(digest, &context);
435   make_digest(md5str, digest);
436   snprintf(s, MAXPATHLEN-1, "%s%s%s", EAG(cache_dir), prefix, md5str);
437   return 1;
438 #else
439   zval retval;
440   zval md5;
441   zval param;
442   zval *params[1];
443
444   ZVAL_STRING(&md5, "md5", 0);
445   INIT_ZVAL(param);
446   params[0] = &param;
447   ZVAL_STRING(params[0], (char*)key, 0);
448   if (call_user_function(CG(function_table), (zval**)NULL, &md5, &retval, 1, params TSRMLS_CC) == SUCCESS &&
449       retval.type == IS_STRING &&
450       retval.value.str.len == 32) {
451     strncpy(s, EAG(cache_dir), MAXPATHLEN-1);
452     strlcat(s, prefix, MAXPATHLEN);
453     strlcat(s, retval.value.str.val, MAXPATHLEN);
454     zval_dtor(&retval);
455     return 1;
456   }
457   s[0] ='\0';
458 #endif
459   return 0;
460 }
461
462 /* Remove expired keys, content and scripts from the cache */
463 void eaccelerator_prune(time_t t) {
464   unsigned int i;
465
466   EACCELERATOR_LOCK_RW();
467   eaccelerator_mm_instance->last_prune = t;
468   for (i = 0; i < MM_HASH_SIZE; i++) {
469     mm_cache_entry **p = &eaccelerator_mm_instance->hash[i];
470     while (*p != NULL) {
471       struct stat buf;
472       if (((*p)->ttl != 0 && (*p)->ttl < t && (*p)->use_cnt <= 0) ||
473           stat((*p)->realfilename,&buf) != 0 ||
474 #ifdef EACCELERATOR_USE_INODE
475           (*p)->st_dev != buf.st_dev ||
476           (*p)->st_ino != buf.st_ino ||
477 #endif
478           (*p)->mtime != buf.st_mtime ||
479           (*p)->filesize != buf.st_size) {
480         mm_cache_entry *r = *p;
481         *p = (*p)->next;
482         eaccelerator_mm_instance->hash_cnt--;
483         eaccelerator_free_nolock(r);
484       } else {
485         p = &(*p)->next;
486       }
487     }
488   }
489   EACCELERATOR_UNLOCK_RW();
490 }
491
492 /* Allocate a new cache chunk */
493 void* eaccelerator_malloc2(size_t size TSRMLS_DC) {
494   void *p = NULL;
495   time_t t;
496
497   if (eaccelerator_gc(TSRMLS_C) > 0) {
498     p = eaccelerator_malloc(size);
499     if (p != NULL) {
500       return p;
501     }
502   }
503   if (eaccelerator_shm_prune_period > 0) {
504     t = time(0);
505     if (t - eaccelerator_mm_instance->last_prune > eaccelerator_shm_prune_period) {
506       eaccelerator_prune(t);
507       p = eaccelerator_malloc(size);
508     }
509   }
510   return p;
511 }
512
513 #define EACCELERATOR_CRC32(crc, ch)   (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff])
514
515 static const unsigned int crc32tab[256] = {
516   0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
517   0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
518   0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
519   0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
520   0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
521   0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
522   0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
523   0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
524   0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
525   0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
526   0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
527   0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
528   0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
529   0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
530   0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
531   0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
532   0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
533   0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
534   0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
535   0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
536   0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
537   0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
538   0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
539   0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
540   0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
541   0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
542   0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
543   0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
544   0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
545   0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
546   0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
547   0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
548   0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
549   0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
550   0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
551   0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
552   0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
553   0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
554   0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
555   0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
556   0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
557   0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
558   0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
559   0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
560   0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
561   0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
562   0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
563   0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
564   0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
565   0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
566   0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
567   0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
568   0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
569   0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
570   0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
571   0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
572   0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
573   0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
574   0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
575   0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
576   0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
577   0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
578   0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
579   0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
580 };
581
582 unsigned int eaccelerator_crc32(const char *p, size_t n) {
583   unsigned int crc = ~0;
584   for (; n--; ++p) {
585     EACCELERATOR_CRC32(crc, *p);
586   }
587   return ~crc;
588 }
589
590 void eaccelerator_fixup (mm_cache_entry * p TSRMLS_DC)
591 {
592   mm_fc_entry *q;
593
594   EAG (mem) = (char *) ((long) p - (long) p->next);
595   EAG (compress) = 1;
596   p->next = NULL;
597   FIXUP (p->op_array);
598   FIXUP (p->f_head);
599   FIXUP (p->c_head);
600   fixup_op_array (p->op_array TSRMLS_CC);
601   q = p->f_head;
602   while (q != NULL) {
603     FIXUP (q->fc);
604     fixup_op_array ((eaccelerator_op_array *) q->fc TSRMLS_CC);
605     FIXUP (q->next);
606     q = q->next;
607   }
608   q = p->c_head;
609   while (q != NULL) {
610     FIXUP (q->fc);
611     fixup_class_entry ((eaccelerator_class_entry *) q->fc TSRMLS_CC);
612     FIXUP (q->next);
613     q = q->next;
614   }
615 }
616
617 /******************************************************************************/
618 /* Cache file functions.                                                                                                          */
619 /* TODO: create cache subdirectories -> speed improvement highly used servers */
620 /******************************************************************************/
621
622 /* Retrieve a cache entry from the cache directory */
623 static mm_cache_entry* hash_find_file(const char  *key,
624                                       struct stat *buf TSRMLS_DC) {
625   int f;
626   char s[MAXPATHLEN];
627   mm_file_header hdr;
628   mm_cache_entry *p;
629   int use_shm = 1;
630
631 #ifdef EACCELERATOR_USE_INODE
632   struct stat buf2;
633
634   if (!eaccelerator_inode_key(s, buf->st_dev, buf->st_ino TSRMLS_CC)) {
635     return NULL;
636   }
637 #else
638   if (!eaccelerator_md5(s, "/eaccelerator-", key TSRMLS_CC)) {
639     return NULL;
640   }
641 #endif
642
643   if ((f = open(s, O_RDONLY | O_BINARY)) > 0) {
644     EACCELERATOR_FLOCK(f, LOCK_SH);
645     if (read(f, &hdr, sizeof(hdr)) != sizeof(hdr)) {
646       EACCELERATOR_FLOCK(f, LOCK_UN);
647       close(f);
648       return NULL;
649     }
650     if (strncmp(hdr.magic,"EACCELERATOR",8) != 0 ||
651         hdr.eaccelerator_version != binary_eaccelerator_version ||
652         hdr.zend_version != binary_zend_version ||
653         hdr.php_version != binary_php_version) {
654       EACCELERATOR_FLOCK(f, LOCK_UN);
655       close(f);
656       unlink(s);
657       return NULL;
658     }
659     p = eaccelerator_malloc(hdr.size);
660     if (p == NULL) {
661       p = eaccelerator_malloc2(hdr.size TSRMLS_CC);
662     }
663     if (p == NULL) {
664       p = emalloc(hdr.size);
665       use_shm = 0;
666     }
667     if (p == NULL) {
668       EACCELERATOR_FLOCK(f, LOCK_UN);
669       close(f);
670       return NULL;
671     }
672     if (read(f, p, hdr.size) != hdr.size ||
673         p->size != hdr.size ||
674         hdr.crc32 != eaccelerator_crc32((const char*)p,p->size)) {
675       EACCELERATOR_FLOCK(f, LOCK_UN);
676       close(f);
677       unlink(s);
678       if (use_shm) eaccelerator_free(p); else efree(p);
679       return NULL;
680     }
681     EACCELERATOR_FLOCK(f, LOCK_UN);
682     close(f);
683 #ifdef EACCELERATOR_USE_INODE
684     if (p->st_dev != buf->st_dev || p->st_ino != buf->st_ino) {
685 #else
686     if (strcmp(key,p->realfilename) != 0) {
687 #endif
688       if (use_shm) eaccelerator_free(p); else efree(p);
689       return NULL;
690     }
691     if ((eaccelerator_check_mtime &&
692         (buf->st_mtime != p->mtime || buf->st_size != p->filesize))
693 #ifdef EACCELERATOR_USE_INODE
694         ||
695         (strcmp(p->realfilename, key) != 0 &&
696          (stat(p->realfilename,&buf2) != 0 ||
697          buf2.st_dev != buf->st_dev ||
698          buf2.st_ino != buf->st_ino))
699 #endif
700        ) {
701       /* key is invalid. Remove it. */
702       if (use_shm) eaccelerator_free(p); else efree(p);
703       unlink(s);
704       return NULL;
705     }
706     eaccelerator_fixup(p TSRMLS_CC);
707     if (use_shm) {
708       p->nhits    = 1;
709       p->nreloads = 1;
710       p->use_cnt  = 1;
711       p->removed  = 0;
712       if (eaccelerator_shm_ttl > 0) {
713         p->ttl = time(0) + eaccelerator_shm_ttl;
714       } else {
715         p->ttl = 0;
716       }
717       hash_add_mm(p);
718     } else {
719       p->use_cnt  = 0;
720       p->removed  = 1;
721     }
722     return p;
723   }
724   return NULL;
725 }
726
727 /* Add a cache entry to the cache directory */
728 static int hash_add_file(mm_cache_entry *p TSRMLS_DC) {
729   int f;
730   int ret = 0;
731   char s[MAXPATHLEN];
732   mm_file_header hdr;
733
734 #ifdef EACCELERATOR_USE_INODE
735   if (!eaccelerator_inode_key(s, p->st_dev, p->st_ino TSRMLS_CC)) {
736     return 0;
737   }
738 #else
739   if (!eaccelerator_md5(s, "/eaccelerator-", p->realfilename TSRMLS_CC)) {
740     return 0;
741   }
742 #endif
743
744   unlink(s);
745   f = open(s, O_CREAT | O_WRONLY | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR);
746   if (f > 0) {
747     EACCELERATOR_FLOCK(f, LOCK_EX);
748     strncpy(hdr.magic, "EACCELERATOR", 8);
749     hdr.eaccelerator_version = binary_eaccelerator_version;
750     hdr.zend_version    = binary_zend_version;
751     hdr.php_version     = binary_php_version;
752     hdr.size  = p->size;
753     hdr.mtime = p->mtime;
754     p->next = p;
755     hdr.crc32 = eaccelerator_crc32((const char*)p,p->size);
756     ret = (write(f, &hdr, sizeof(hdr)) == sizeof(hdr));
757     if (ret) ret = (write(f, p, p->size) == p->size);
758     EACCELERATOR_FLOCK(f, LOCK_UN);
759     close(f);
760   }
761   return ret;
762 }
763
764 /* Create a cache entry from the given op_array, functions and classes of a
765    script */
766 static mm_cache_entry *eaccelerator_store_int (char *key, int len,
767         zend_op_array * op_array, Bucket * f, Bucket * c TSRMLS_DC)
768 {
769   mm_cache_entry *p;
770   mm_fc_entry *fc;
771   mm_fc_entry *q;
772   char *x;
773
774   ea_debug_pad (EA_DEBUG TSRMLS_CC);
775   ea_debug_printf (EA_DEBUG, "[%d] eaccelerator_store_int: key='%s'\n",
776           getpid (), key);
777
778   EAG (compress) = 1;
779   zend_hash_init (&EAG (strings), 0, NULL, NULL, 0);
780   p = (mm_cache_entry *) EAG (mem);
781   EAG (mem) += offsetof (mm_cache_entry, realfilename) + len + 1;
782
783   p->nhits = 0;
784   p->use_cnt = 0;
785   p->removed = 0;
786   p->f_head = NULL;
787   p->c_head = NULL;
788   memcpy (p->realfilename, key, len + 1);
789   x = p->realfilename;
790   zend_hash_add (&EAG (strings), key, len + 1, &x, sizeof (char *), NULL);
791
792   q = NULL;
793   while (c != NULL) {
794     ea_debug_pad (EA_DEBUG TSRMLS_CC);
795     ea_debug_printf (EA_DEBUG,
796             "[%d] eaccelerator_store_int:     class hashkey=", getpid ());
797     ea_debug_binary_print (EA_DEBUG, c->arKey, c->nKeyLength);
798
799     EACCELERATOR_ALIGN (EAG (mem));
800     fc = (mm_fc_entry *) EAG (mem);
801     EAG (mem) += offsetof (mm_fc_entry, htabkey) + c->nKeyLength;
802     memcpy (fc->htabkey, c->arKey, c->nKeyLength);
803     fc->htablen = c->nKeyLength;
804     fc->next = NULL;
805 #ifdef ZEND_ENGINE_2
806     fc->fc = *(zend_class_entry **) c->pData;
807 #else
808     fc->fc = c->pData;
809 #endif
810     c = c->pListNext;
811     x = fc->htabkey;
812     zend_hash_add (&EAG (strings), fc->htabkey, fc->htablen, &x,
813             sizeof (char *), NULL);
814     if (q == NULL) {
815       p->c_head = fc;
816     } else {
817       q->next = fc;
818     }
819     q = fc;
820   }
821
822   q = NULL;
823   while (f != NULL) {
824       ea_debug_pad (EA_DEBUG TSRMLS_CC);
825       ea_debug_printf (EA_DEBUG,
826               "[%d] eaccelerator_store_int:     function hashkey='%s'\n", getpid (), f->arKey);
827
828       EACCELERATOR_ALIGN (EAG (mem));
829       fc = (mm_fc_entry *) EAG (mem);
830       EAG (mem) += offsetof (mm_fc_entry, htabkey) + f->nKeyLength;
831       memcpy (fc->htabkey, f->arKey, f->nKeyLength);
832       fc->htablen = f->nKeyLength;
833       fc->next = NULL;
834       fc->fc = f->pData;
835       f = f->pListNext;
836       x = fc->htabkey;
837       zend_hash_add (&EAG (strings), fc->htabkey, fc->htablen, &x,
838               sizeof (char *), NULL);
839       if (q == NULL) {
840           p->f_head = fc;
841       } else {
842           q->next = fc;
843       }
844       q = fc;
845   }
846
847   q = p->c_head;
848   while (q != NULL) {
849 #ifdef ZEND_ENGINE_2
850       q->fc = store_class_entry ((zend_class_entry *) q->fc TSRMLS_CC);
851 #else
852       q->fc = store_class_entry ((zend_class_entry *) q->fc TSRMLS_CC);
853 #endif
854       q = q->next;
855   }
856
857   q = p->f_head;
858   while (q != NULL) {
859       q->fc = store_op_array ((zend_op_array *) q->fc TSRMLS_CC);
860       q = q->next;
861   }
862   p->op_array = store_op_array (op_array TSRMLS_CC);
863
864   zend_hash_destroy (&EAG (strings));
865   return p;
866 }
867
868 /* called after succesful compilation, from eaccelerator_compile file */
869 /* Adds the data from the compilation of the script to the cache */
870 static int eaccelerator_store(char* key, struct stat *buf, int nreloads,
871                          zend_op_array* op_array,
872                          Bucket* f, Bucket *c TSRMLS_DC) {
873   mm_cache_entry *p;
874   int len = strlen(key);
875   int use_shm = 1;
876   int ret = 0;
877   int size = 0;
878
879   zend_try {
880     size = calc_size(key, op_array, f, c TSRMLS_CC);
881   } zend_catch {
882     size =  0;
883   } zend_end_try();
884   if (size == 0) {
885     return 0;
886   }
887   EACCELERATOR_UNPROTECT();
888   EAG(mem) = eaccelerator_malloc(size);
889   if (EAG(mem) == NULL) {
890     EAG(mem) = eaccelerator_malloc2(size TSRMLS_CC);
891   }
892   if (!EAG(mem) && !eaccelerator_scripts_shm_only) {
893     EACCELERATOR_PROTECT();
894     EAG(mem) = emalloc(size);
895     use_shm = 0;
896   }
897   if (EAG(mem)) {
898     memset(EAG(mem), 0, size);
899     p = eaccelerator_store_int(key, len, op_array, f, c TSRMLS_CC);
900     p->mtime    = buf->st_mtime;
901     p->filesize = buf->st_size;
902     p->size     = size;
903     p->nreloads = nreloads;
904 #ifdef EACCELERATOR_USE_INODE
905     p->st_dev   = buf->st_dev;
906     p->st_ino   = buf->st_ino;
907 #endif
908     if (use_shm) {
909       if (eaccelerator_shm_ttl > 0) {
910         p->ttl = time(0) + eaccelerator_shm_ttl;
911       } else {
912         p->ttl = 0;
913       }
914       if (!eaccelerator_scripts_shm_only) {
915         hash_add_file(p TSRMLS_CC);
916       }
917       hash_add_mm(p);
918       EACCELERATOR_PROTECT();
919       ret = 1;
920     } else {
921       ret =  hash_add_file(p TSRMLS_CC);
922       efree(p);
923     }
924   }
925   return ret;
926 }
927
928 /* Try to restore a file from the cache. If the file isn't found in memory, the
929    the disk cache is checked */
930 static zend_op_array* eaccelerator_restore(char *realname, struct stat *buf,
931                                       int *nreloads, time_t compile_time TSRMLS_DC) {
932   mm_cache_entry *p;
933   zend_op_array *op_array = NULL;
934
935   *nreloads = 1;
936   EACCELERATOR_UNPROTECT();
937   p = hash_find_mm(realname, buf, nreloads,
938                    ((eaccelerator_shm_ttl > 0)?(compile_time + eaccelerator_shm_ttl):0));
939   if (p == NULL && !eaccelerator_scripts_shm_only) {
940     p = hash_find_file(realname, buf TSRMLS_CC);
941   }
942   EACCELERATOR_PROTECT();
943   if (p != NULL && p->op_array != NULL) {
944     EAG(class_entry) = NULL;
945     op_array = restore_op_array(NULL, p->op_array TSRMLS_CC);
946     if (op_array != NULL) {
947       mm_fc_entry *e;
948       mm_used_entry *used = emalloc(sizeof(mm_used_entry));
949       used->entry  = p;
950       used->next   = (mm_used_entry*)EAG(used_entries);
951       EAG(used_entries) = (void*)used;
952       EAG(mem) = op_array->filename;
953       for (e = p->c_head; e!=NULL; e = e->next) {
954         restore_class(e TSRMLS_CC);
955       }
956       for (e = p->f_head; e!=NULL; e = e->next) {
957         restore_function(e TSRMLS_CC);
958       }
959       EAG(mem) = p->realfilename;
960     }
961   }
962   return op_array;
963 }
964
965 /*
966  * Only files matching user specified conditions should be cached.
967  *
968  * TODO - check the algorithm (fl)
969  */
970 static int match(const char* name, const char* pat) {
971   char p,k;
972   int ok, neg;
973
974   while (1) {
975     p = *pat++;
976     if (p == '\0') {
977       return (*name == '\0');
978     } else if (p == '*') {
979       if (*pat == '\0') {
980         return 1;
981       }
982       do {
983         if (match(name, pat)) {
984           return 1;
985         }
986       } while (*name++ != '\0');
987       return 0;
988     } else if (p == '?') {
989       if (*name++ == '\0') {
990         return 0;
991       }
992     } else if (p == '[') {
993       ok = 0;
994       if ((k = *name++) == '\0') {
995         return 0;
996       }
997       if ((neg = (*pat == '!')) != '\0') {
998         ++pat;
999       }
1000       while ((p = *pat++) != ']') {
1001         if (*pat == '-') {
1002           if (p <= k && k <= pat[1]) {
1003             ok = 1;
1004           }
1005           pat += 2;
1006         } else {
1007           if (p == '\\') {
1008             p = *pat++;
1009             if (p == '\0') {
1010               p ='\\';
1011               pat--;
1012             }
1013           }
1014           if (p == k) {
1015             ok = 1;
1016           }
1017         }
1018       }
1019       if (ok == neg) {
1020         return 0;
1021       }
1022     } else {
1023       if (p == '\\') {
1024         p = *pat++;
1025         if (p == '\0') {
1026           p ='\\';
1027           pat--;
1028         }
1029       }
1030       if (*name++ != p) {
1031         return 0;
1032       }
1033     }
1034   }
1035   return (*name == '\0');
1036 }
1037
1038 /* Check if the file is ok to cache */
1039 static int eaccelerator_ok_to_cache(char *realname TSRMLS_DC) {
1040   mm_cond_entry *p;
1041   int ok;
1042
1043   if (EAG(cond_list) == NULL) {
1044     return 1;
1045   }
1046
1047   /* if "realname" matches to any pattern started with "!" then ignore it */
1048   for (p = EAG(cond_list); p != NULL; p = p->next) {
1049     if (p->not && match(realname, p->str)) {
1050       return 0;
1051     }
1052   }
1053
1054   /* else if it matches to any pattern not started with "!" then accept it */
1055   ok = 1;
1056   for (p = EAG(cond_list); p != NULL; p = p->next) {
1057     if (!p->not) {
1058       ok = 0;
1059       if (match(realname, p->str)) {
1060         return 1;
1061       }
1062     }
1063   }
1064   return ok;
1065 }
1066
1067 static char* eaccelerator_realpath(const char* name, char* realname TSRMLS_DC) {
1068 /* ???TODO it is possibe to cache name->realname mapping to avoid lstat() calls */
1069 #if ZEND_MODULE_API_NO >= 20001222
1070   return VCWD_REALPATH(name, realname);
1071 #else
1072   return V_REALPATH(name, realname);
1073 #endif
1074 }
1075
1076 static int eaccelerator_stat(zend_file_handle *file_handle,
1077                         char* realname, struct stat* buf TSRMLS_DC) {
1078 #ifdef EACCELERATOR_USE_INODE
1079 #ifndef ZEND_WIN32
1080   if (file_handle->type == ZEND_HANDLE_FP && file_handle->handle.fp != NULL) {
1081     if (fstat(fileno(file_handle->handle.fp), buf) == 0 &&
1082        S_ISREG(buf->st_mode)) {
1083       if (file_handle->opened_path != NULL) {
1084         strcpy(realname,file_handle->opened_path);
1085       }
1086       return 0;
1087     }
1088   } else
1089 #endif
1090   if (file_handle->opened_path != NULL) {
1091     if (stat(file_handle->opened_path, buf) == 0 &&
1092         S_ISREG(buf->st_mode)) {
1093        strcpy(realname,file_handle->opened_path);
1094        return 0;
1095     }
1096   } else if (PG(include_path) == NULL ||
1097              file_handle->filename[0] == '.' ||
1098              IS_SLASH(file_handle->filename[0]) ||
1099              IS_ABSOLUTE_PATH(file_handle->filename,strlen(file_handle->filename))) {
1100     if (stat(file_handle->filename, buf) == 0 &&
1101         S_ISREG(buf->st_mode)) {
1102        return 0;
1103     }
1104   } else {
1105     char* ptr = PG(include_path);
1106     char* end;
1107     int   len;
1108     char  tryname[MAXPATHLEN];
1109     int   filename_len = strlen(file_handle->filename);
1110
1111     while (ptr && *ptr) {
1112       end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
1113       if (end != NULL) {
1114         len = end-ptr;
1115         end++;
1116       } else {
1117         len = strlen(ptr);
1118         end = ptr+len;
1119       }
1120       if (len+filename_len+2 < MAXPATHLEN) {
1121         memcpy(tryname, ptr, len);
1122         tryname[len] = '/';
1123         memcpy(tryname+len+1, file_handle->filename, filename_len);
1124         tryname[len+filename_len+1] = '\0';
1125         if (stat(tryname, buf) == 0 &&
1126             S_ISREG(buf->st_mode)) {
1127           return 0;
1128         }
1129       }
1130       ptr = end;
1131     }
1132
1133         if (zend_is_executing(TSRMLS_C)) {
1134                 strncpy(tryname, zend_get_executed_filename(TSRMLS_C), MAXPATHLEN);
1135                 tryname[MAXPATHLEN - 1] = 0;
1136                 int tryname_length = strlen(tryname);
1137
1138                 while (tryname_length >= 0 && !IS_SLASH(tryname[tryname_length]))
1139                         tryname_length--;
1140                 if (tryname_length > 0 && tryname[0] != '[' // [no active file]
1141                         && tryname_length + filename_len + 1 < MAXPATHLEN)
1142                 {
1143                         strncpy(tryname + tryname_length + 1, file_handle->filename, filename_len + 1);
1144                         if (stat(tryname, buf) == 0 && S_ISREG(buf->st_mode))
1145                                 return 0;
1146                 }
1147         }
1148   }
1149   return -1;
1150 #else
1151   if (file_handle->opened_path != NULL) {
1152     strcpy(realname,file_handle->opened_path);
1153 #ifndef ZEND_WIN32
1154     if (file_handle->type == ZEND_HANDLE_FP && file_handle->handle.fp != NULL) {
1155       if (!eaccelerator_check_mtime) {
1156         return 0;
1157       } else if (fstat(fileno(file_handle->handle.fp), buf) == 0 &&
1158                  S_ISREG(buf->st_mode)) {
1159         return 0;
1160       } else {
1161         return -1;
1162       }
1163     } else {
1164       if (!eaccelerator_check_mtime) {
1165         return 0;
1166       } else if (stat(realname, buf) == 0 &&
1167                  S_ISREG(buf->st_mode)) {
1168         return 0;
1169       } else {
1170         return -1;
1171       }
1172     }
1173 #else
1174     if (!eaccelerator_check_mtime) {
1175       return 0;
1176     } else if (stat(realname, buf) == 0 &&
1177                S_ISREG(buf->st_mode)) {
1178       return 0;
1179     } else {
1180       return -1;
1181     }
1182 #endif
1183   } else if (file_handle->filename == NULL) {
1184     return -1;
1185   } else if (PG(include_path) == NULL ||
1186              file_handle->filename[0] == '.' ||
1187              IS_SLASH(file_handle->filename[0]) ||
1188              IS_ABSOLUTE_PATH(file_handle->filename,strlen(file_handle->filename))) {
1189     if (eaccelerator_realpath(file_handle->filename, realname TSRMLS_CC)) {
1190       if (!eaccelerator_check_mtime) {
1191         return 0;
1192       } else if (stat(realname, buf) == 0 &&
1193                  S_ISREG(buf->st_mode)) {
1194         return 0;
1195       } else {
1196         return -1;
1197       }
1198     }
1199   } else {
1200     char* ptr = PG(include_path);
1201     char* end;
1202     int   len;
1203     char  tryname[MAXPATHLEN];
1204     int   filename_len = strlen(file_handle->filename);
1205
1206     while (ptr && *ptr) {
1207       end = strchr(ptr, DEFAULT_DIR_SEPARATOR);