root/eaccelerator/branches/0.9.4/eaccelerator.c

Revision 156, 85.6 kB (checked in by zoeloelip, 3 years ago)

On failure of mm initialisation, return FAILURE instead of disabling eAccelerator.

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