1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Dmitry Stogov <dmitry@zend.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "zend.h"
20 #include "zend_virtual_cwd.h"
21 #include "zend_compile.h"
22 #include "zend_vm.h"
23 #include "zend_interfaces.h"
24
25 #include "php.h"
26 #ifdef ZEND_WIN32
27 #include "ext/standard/md5.h"
28 #endif
29
30 #ifdef HAVE_OPCACHE_FILE_CACHE
31
32 #include "ZendAccelerator.h"
33 #include "zend_file_cache.h"
34 #include "zend_shared_alloc.h"
35 #include "zend_accelerator_util_funcs.h"
36 #include "zend_accelerator_hash.h"
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41
42 #if HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45
46 #ifdef HAVE_SYS_UIO_H
47 # include <sys/uio.h>
48 #endif
49
50 #ifdef HAVE_SYS_FILE_H
51 # include <sys/file.h>
52 #endif
53
54 #ifdef ZEND_WIN32
55 # define LOCK_SH 0
56 # define LOCK_EX 1
57 # define LOCK_UN 2
zend_file_cache_flock(int fd,int op)58 static int zend_file_cache_flock(int fd, int op)
59 {
60 OVERLAPPED offset = {0,0,0,0,NULL};
61 if (op == LOCK_EX) {
62 if (LockFileEx((HANDLE)_get_osfhandle(fd),
63 LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset) == TRUE) {
64 return 0;
65 }
66 } else if (op == LOCK_SH) {
67 if (LockFileEx((HANDLE)_get_osfhandle(fd),
68 0, 0, 1, 0, &offset) == TRUE) {
69 return 0;
70 }
71 } else if (op == LOCK_UN) {
72 if (UnlockFileEx((HANDLE)_get_osfhandle(fd),
73 0, 1, 0, &offset) == TRUE) {
74 return 0;
75 }
76 }
77 return -1;
78 }
79 #elif defined(HAVE_FLOCK)
80 # define zend_file_cache_flock flock
81 #else
82 # define LOCK_SH 0
83 # define LOCK_EX 1
84 # define LOCK_UN 2
zend_file_cache_flock(int fd,int type)85 static int zend_file_cache_flock(int fd, int type)
86 {
87 return 0;
88 }
89 #endif
90
91 #ifndef O_BINARY
92 # define O_BINARY 0
93 #endif
94
95 #define SUFFIX ".bin"
96
97 #define IS_SERIALIZED_INTERNED(ptr) \
98 ((size_t)(ptr) & Z_UL(1))
99
100 /* Allowing == here to account for a potential empty allocation at the end of the memory */
101 #define IS_SERIALIZED(ptr) \
102 ((char*)(ptr) <= (char*)script->size)
103 #define IS_UNSERIALIZED(ptr) \
104 (((char*)(ptr) >= (char*)script->mem && (char*)(ptr) < (char*)script->mem + script->size) || \
105 IS_ACCEL_INTERNED(ptr))
106 #define SERIALIZE_PTR(ptr) do { \
107 if (ptr) { \
108 ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
109 (ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
110 } \
111 } while (0)
112 #define UNSERIALIZE_PTR(ptr) do { \
113 if (ptr) { \
114 ZEND_ASSERT(IS_SERIALIZED(ptr)); \
115 (ptr) = (void*)((char*)buf + (size_t)(ptr)); \
116 } \
117 } while (0)
118 #define SERIALIZE_STR(ptr) do { \
119 if (ptr) { \
120 if (IS_ACCEL_INTERNED(ptr)) { \
121 (ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
122 } else { \
123 ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
124 /* script->corrupted shows if the script in SHM or not */ \
125 if (EXPECTED(script->corrupted)) { \
126 GC_FLAGS(ptr) |= IS_STR_INTERNED; \
127 GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \
128 } \
129 (ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
130 } \
131 } \
132 } while (0)
133 #define UNSERIALIZE_STR(ptr) do { \
134 if (ptr) { \
135 if (IS_SERIALIZED_INTERNED(ptr)) { \
136 (ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), !script->corrupted); \
137 } else { \
138 ZEND_ASSERT(IS_SERIALIZED(ptr)); \
139 (ptr) = (void*)((char*)buf + (size_t)(ptr)); \
140 /* script->corrupted shows if the script in SHM or not */ \
141 if (EXPECTED(!script->corrupted)) { \
142 GC_FLAGS(ptr) |= IS_STR_INTERNED | IS_STR_PERMANENT; \
143 } else { \
144 GC_FLAGS(ptr) |= IS_STR_INTERNED; \
145 GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \
146 } \
147 } \
148 } \
149 } while (0)
150
151 static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
152 {HT_INVALID_IDX, HT_INVALID_IDX};
153
154 typedef struct _zend_file_cache_metainfo {
155 char magic[8];
156 char system_id[32];
157 size_t mem_size;
158 size_t str_size;
159 size_t script_offset;
160 accel_time_t timestamp;
161 uint32_t checksum;
162 } zend_file_cache_metainfo;
163
zend_file_cache_mkdir(char * filename,size_t start)164 static int zend_file_cache_mkdir(char *filename, size_t start)
165 {
166 char *s = filename + start;
167
168 while (*s) {
169 if (IS_SLASH(*s)) {
170 char old = *s;
171 *s = '\000';
172 #ifndef ZEND_WIN32
173 if (mkdir(filename, S_IRWXU) < 0 && errno != EEXIST) {
174 #else
175 if (php_win32_ioutil_mkdir(filename, 0700) < 0 && errno != EEXIST) {
176 #endif
177 *s = old;
178 return FAILURE;
179 }
180 *s = old;
181 }
182 s++;
183 }
184 return SUCCESS;
185 }
186
187 typedef void (*serialize_callback_t)(zval *zv,
188 zend_persistent_script *script,
189 zend_file_cache_metainfo *info,
190 void *buf);
191
192 typedef void (*unserialize_callback_t)(zval *zv,
193 zend_persistent_script *script,
194 void *buf);
195
196 static void zend_file_cache_serialize_zval(zval *zv,
197 zend_persistent_script *script,
198 zend_file_cache_metainfo *info,
199 void *buf);
200 static void zend_file_cache_unserialize_zval(zval *zv,
201 zend_persistent_script *script,
202 void *buf);
203
204 static void *zend_file_cache_serialize_interned(zend_string *str,
205 zend_file_cache_metainfo *info)
206 {
207 size_t len;
208 void *ret;
209
210 /* check if the same interned string was already stored */
211 ret = zend_shared_alloc_get_xlat_entry(str);
212 if (ret) {
213 return ret;
214 }
215
216 len = ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(ZSTR_LEN(str)));
217 ret = (void*)(info->str_size | Z_UL(1));
218 zend_shared_alloc_register_xlat_entry(str, ret);
219 if (info->str_size + len > ZSTR_LEN((zend_string*)ZCG(mem))) {
220 size_t new_len = info->str_size + len;
221 ZCG(mem) = (void*)zend_string_realloc(
222 (zend_string*)ZCG(mem),
223 ((_ZSTR_HEADER_SIZE + 1 + new_len + 4095) & ~0xfff) - (_ZSTR_HEADER_SIZE + 1),
224 0);
225 }
226 memcpy(ZSTR_VAL((zend_string*)ZCG(mem)) + info->str_size, str, len);
227 info->str_size += len;
228 return ret;
229 }
230
231 static void *zend_file_cache_unserialize_interned(zend_string *str, int in_shm)
232 {
233 zend_string *ret;
234
235 str = (zend_string*)((char*)ZCG(mem) + ((size_t)(str) & ~Z_UL(1)));
236 if (in_shm) {
237 ret = accel_new_interned_string(str);
238 if (ret == str) {
239 /* We have to create new SHM allocated string */
240 size_t size = _ZSTR_STRUCT_SIZE(ZSTR_LEN(str));
241 ret = zend_shared_alloc(size);
242 if (!ret) {
243 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
244 LONGJMP(*EG(bailout), FAILURE);
245 }
246 memcpy(ret, str, size);
247 /* String wasn't interned but we will use it as interned anyway */
248 GC_REFCOUNT(ret) = 1;
249 GC_TYPE_INFO(ret) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERSISTENT | IS_STR_PERMANENT) << 8);
250 }
251 } else {
252 ret = str;
253 GC_FLAGS(ret) |= IS_STR_INTERNED;
254 GC_FLAGS(ret) &= ~IS_STR_PERMANENT;
255 }
256 return ret;
257 }
258
259 static void zend_file_cache_serialize_hash(HashTable *ht,
260 zend_persistent_script *script,
261 zend_file_cache_metainfo *info,
262 void *buf,
263 serialize_callback_t func)
264 {
265 Bucket *p, *end;
266
267 if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
268 ht->arData = NULL;
269 return;
270 }
271 if (IS_SERIALIZED(ht->arData)) {
272 return;
273 }
274 SERIALIZE_PTR(ht->arData);
275 p = ht->arData;
276 UNSERIALIZE_PTR(p);
277 end = p + ht->nNumUsed;
278 while (p < end) {
279 if (Z_TYPE(p->val) != IS_UNDEF) {
280 SERIALIZE_STR(p->key);
281 func(&p->val, script, info, buf);
282 }
283 p++;
284 }
285 }
286
287 static zend_ast *zend_file_cache_serialize_ast(zend_ast *ast,
288 zend_persistent_script *script,
289 zend_file_cache_metainfo *info,
290 void *buf)
291 {
292 uint32_t i;
293 zend_ast *ret;
294
295 SERIALIZE_PTR(ast);
296 ret = ast;
297 UNSERIALIZE_PTR(ast);
298
299 if (ast->kind == ZEND_AST_ZVAL) {
300 zend_file_cache_serialize_zval(&((zend_ast_zval*)ast)->val, script, info, buf);
301 } else if (zend_ast_is_list(ast)) {
302 zend_ast_list *list = zend_ast_get_list(ast);
303 for (i = 0; i < list->children; i++) {
304 if (list->child[i]) {
305 list->child[i] = zend_file_cache_serialize_ast(list->child[i], script, info, buf);
306 }
307 }
308 } else {
309 uint32_t children = zend_ast_get_num_children(ast);
310 for (i = 0; i < children; i++) {
311 if (ast->child[i]) {
312 ast->child[i] = zend_file_cache_serialize_ast(ast->child[i], script, info, buf);
313 }
314 }
315 }
316 return ret;
317 }
318
319 static void zend_file_cache_serialize_zval(zval *zv,
320 zend_persistent_script *script,
321 zend_file_cache_metainfo *info,
322 void *buf)
323 {
324 switch (Z_TYPE_P(zv)) {
325 case IS_STRING:
326 case IS_CONSTANT:
327 if (!IS_SERIALIZED(Z_STR_P(zv))) {
328 SERIALIZE_STR(Z_STR_P(zv));
329 }
330 break;
331 case IS_ARRAY:
332 if (!IS_SERIALIZED(Z_ARR_P(zv))) {
333 HashTable *ht;
334
335 SERIALIZE_PTR(Z_ARR_P(zv));
336 ht = Z_ARR_P(zv);
337 UNSERIALIZE_PTR(ht);
338 zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
339 }
340 break;
341 case IS_REFERENCE:
342 if (!IS_SERIALIZED(Z_REF_P(zv))) {
343 zend_reference *ref;
344
345 SERIALIZE_PTR(Z_REF_P(zv));
346 ref = Z_REF_P(zv);
347 UNSERIALIZE_PTR(ref);
348 zend_file_cache_serialize_zval(&ref->val, script, info, buf);
349 }
350 break;
351 case IS_CONSTANT_AST:
352 if (!IS_SERIALIZED(Z_AST_P(zv))) {
353 zend_ast_ref *ast;
354
355 SERIALIZE_PTR(Z_AST_P(zv));
356 ast = Z_AST_P(zv);
357 UNSERIALIZE_PTR(ast);
358 if (!IS_SERIALIZED(ast->ast)) {
359 ast->ast = zend_file_cache_serialize_ast(ast->ast, script, info, buf);
360 }
361 }
362 break;
363 }
364 }
365
366 static void zend_file_cache_serialize_op_array(zend_op_array *op_array,
367 zend_persistent_script *script,
368 zend_file_cache_metainfo *info,
369 void *buf)
370 {
371 if (op_array->static_variables && !IS_SERIALIZED(op_array->static_variables)) {
372 HashTable *ht;
373
374 SERIALIZE_PTR(op_array->static_variables);
375 ht = op_array->static_variables;
376 UNSERIALIZE_PTR(ht);
377 zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
378 }
379
380 if (op_array->scope && !IS_SERIALIZED(op_array->opcodes)) {
381 if (UNEXPECTED(zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
382 op_array->refcount = (uint32_t*)(intptr_t)-1;
383 SERIALIZE_PTR(op_array->literals);
384 SERIALIZE_PTR(op_array->opcodes);
385 SERIALIZE_PTR(op_array->arg_info);
386 SERIALIZE_PTR(op_array->vars);
387 SERIALIZE_STR(op_array->function_name);
388 SERIALIZE_STR(op_array->filename);
389 SERIALIZE_PTR(op_array->live_range);
390 SERIALIZE_PTR(op_array->scope);
391 SERIALIZE_STR(op_array->doc_comment);
392 SERIALIZE_PTR(op_array->try_catch_array);
393 SERIALIZE_PTR(op_array->prototype);
394 return;
395 }
396 zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes);
397 }
398
399 if (op_array->literals && !IS_SERIALIZED(op_array->literals)) {
400 zval *p, *end;
401
402 SERIALIZE_PTR(op_array->literals);
403 p = op_array->literals;
404 UNSERIALIZE_PTR(p);
405 end = p + op_array->last_literal;
406 while (p < end) {
407 zend_file_cache_serialize_zval(p, script, info, buf);
408 p++;
409 }
410 }
411
412 if (!IS_SERIALIZED(op_array->opcodes)) {
413 zend_op *opline, *end;
414
415 SERIALIZE_PTR(op_array->opcodes);
416 opline = op_array->opcodes;
417 UNSERIALIZE_PTR(opline);
418 end = opline + op_array->last;
419 while (opline < end) {
420 #if ZEND_USE_ABS_CONST_ADDR
421 if (opline->op1_type == IS_CONST) {
422 SERIALIZE_PTR(opline->op1.zv);
423 }
424 if (opline->op2_type == IS_CONST) {
425 SERIALIZE_PTR(opline->op2.zv);
426 }
427 #endif
428 #if ZEND_USE_ABS_JMP_ADDR
429 switch (opline->opcode) {
430 case ZEND_JMP:
431 case ZEND_FAST_CALL:
432 SERIALIZE_PTR(opline->op1.jmp_addr);
433 break;
434 case ZEND_JMPZNZ:
435 /* relative extended_value don't have to be changed */
436 /* break omitted intentionally */
437 case ZEND_JMPZ:
438 case ZEND_JMPNZ:
439 case ZEND_JMPZ_EX:
440 case ZEND_JMPNZ_EX:
441 case ZEND_JMP_SET:
442 case ZEND_COALESCE:
443 case ZEND_FE_RESET_R:
444 case ZEND_FE_RESET_RW:
445 case ZEND_ASSERT_CHECK:
446 SERIALIZE_PTR(opline->op2.jmp_addr);
447 break;
448 case ZEND_DECLARE_ANON_CLASS:
449 case ZEND_DECLARE_ANON_INHERITED_CLASS:
450 case ZEND_FE_FETCH_R:
451 case ZEND_FE_FETCH_RW:
452 case ZEND_SWITCH_LONG:
453 case ZEND_SWITCH_STRING:
454 /* relative extended_value don't have to be changed */
455 break;
456 }
457 #endif
458 zend_serialize_opcode_handler(opline);
459 opline++;
460 }
461
462 if (op_array->arg_info) {
463 zend_arg_info *p, *end;
464 SERIALIZE_PTR(op_array->arg_info);
465 p = op_array->arg_info;
466 UNSERIALIZE_PTR(p);
467 end = p + op_array->num_args;
468 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
469 p--;
470 }
471 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
472 end++;
473 }
474 while (p < end) {
475 if (!IS_SERIALIZED(p->name)) {
476 SERIALIZE_STR(p->name);
477 }
478 if (ZEND_TYPE_IS_CLASS(p->type)) {
479 zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(p->type);
480 zend_string *type_name = ZEND_TYPE_NAME(p->type);
481
482 SERIALIZE_STR(type_name);
483 p->type =
484 (Z_UL(1) << (sizeof(zend_type)*8-1)) | /* type is class */
485 (allow_null ? (Z_UL(1) << (sizeof(zend_type)*8-2)) : Z_UL(0)) | /* type allow null */
486 (zend_type)type_name;
487 }
488 p++;
489 }
490 }
491
492 if (op_array->vars) {
493 zend_string **p, **end;
494
495 SERIALIZE_PTR(op_array->vars);
496 p = op_array->vars;
497 UNSERIALIZE_PTR(p);
498 end = p + op_array->last_var;
499 while (p < end) {
500 if (!IS_SERIALIZED(*p)) {
501 SERIALIZE_STR(*p);
502 }
503 p++;
504 }
505 }
506
507 SERIALIZE_STR(op_array->function_name);
508 SERIALIZE_STR(op_array->filename);
509 SERIALIZE_PTR(op_array->live_range);
510 SERIALIZE_PTR(op_array->scope);
511 SERIALIZE_STR(op_array->doc_comment);
512 SERIALIZE_PTR(op_array->try_catch_array);
513 SERIALIZE_PTR(op_array->prototype);
514 }
515 }
516
517 static void zend_file_cache_serialize_func(zval *zv,
518 zend_persistent_script *script,
519 zend_file_cache_metainfo *info,
520 void *buf)
521 {
522 zend_op_array *op_array;
523
524 SERIALIZE_PTR(Z_PTR_P(zv));
525 op_array = Z_PTR_P(zv);
526 UNSERIALIZE_PTR(op_array);
527 zend_file_cache_serialize_op_array(op_array, script, info, buf);
528 }
529
530 static void zend_file_cache_serialize_prop_info(zval *zv,
531 zend_persistent_script *script,
532 zend_file_cache_metainfo *info,
533 void *buf)
534 {
535 if (!IS_SERIALIZED(Z_PTR_P(zv))) {
536 zend_property_info *prop;
537
538 SERIALIZE_PTR(Z_PTR_P(zv));
539 prop = Z_PTR_P(zv);
540 UNSERIALIZE_PTR(prop);
541
542 ZEND_ASSERT(prop->ce != NULL && prop->name != NULL);
543 if (!IS_SERIALIZED(prop->ce)) {
544 SERIALIZE_PTR(prop->ce);
545 SERIALIZE_STR(prop->name);
546 if (prop->doc_comment) {
547 SERIALIZE_STR(prop->doc_comment);
548 }
549 }
550 }
551 }
552
553 static void zend_file_cache_serialize_class_constant(zval *zv,
554 zend_persistent_script *script,
555 zend_file_cache_metainfo *info,
556 void *buf)
557 {
558 if (!IS_SERIALIZED(Z_PTR_P(zv))) {
559 zend_class_constant *c;
560
561 SERIALIZE_PTR(Z_PTR_P(zv));
562 c = Z_PTR_P(zv);
563 UNSERIALIZE_PTR(c);
564
565 ZEND_ASSERT(c->ce != NULL);
566 if (!IS_SERIALIZED(c->ce)) {
567 SERIALIZE_PTR(c->ce);
568
569 zend_file_cache_serialize_zval(&c->value, script, info, buf);
570
571 if (c->doc_comment) {
572 SERIALIZE_STR(c->doc_comment);
573 }
574 }
575 }
576 }
577
578 static void zend_file_cache_serialize_class(zval *zv,
579 zend_persistent_script *script,
580 zend_file_cache_metainfo *info,
581 void *buf)
582 {
583 zend_class_entry *ce;
584
585 SERIALIZE_PTR(Z_PTR_P(zv));
586 ce = Z_PTR_P(zv);
587 UNSERIALIZE_PTR(ce);
588
589 SERIALIZE_STR(ce->name);
590 zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func);
591 if (ce->default_properties_table) {
592 zval *p, *end;
593
594 SERIALIZE_PTR(ce->default_properties_table);
595 p = ce->default_properties_table;
596 UNSERIALIZE_PTR(p);
597 end = p + ce->default_properties_count;
598 while (p < end) {
599 zend_file_cache_serialize_zval(p, script, info, buf);
600 p++;
601 }
602 }
603 if (ce->default_static_members_table) {
604 zval *p, *end;
605
606 SERIALIZE_PTR(ce->default_static_members_table);
607 p = ce->default_static_members_table;
608 UNSERIALIZE_PTR(p);
609 end = p + ce->default_static_members_count;
610 while (p < end) {
611 zend_file_cache_serialize_zval(p, script, info, buf);
612 p++;
613 }
614 }
615 zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant);
616 SERIALIZE_STR(ce->info.user.filename);
617 SERIALIZE_STR(ce->info.user.doc_comment);
618 zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
619
620 if (ce->trait_aliases) {
621 zend_trait_alias **p, *q;
622
623 SERIALIZE_PTR(ce->trait_aliases);
624 p = ce->trait_aliases;
625 UNSERIALIZE_PTR(p);
626
627 while (*p) {
628 SERIALIZE_PTR(*p);
629 q = *p;
630 UNSERIALIZE_PTR(q);
631
632 if (q->trait_method) {
633 zend_trait_method_reference *m;
634
635 SERIALIZE_PTR(q->trait_method);
636 m = q->trait_method;
637 UNSERIALIZE_PTR(m);
638
639 if (m->method_name) {
640 SERIALIZE_STR(m->method_name);
641 }
642 if (m->class_name) {
643 SERIALIZE_STR(m->class_name);
644 }
645 }
646
647 if (q->alias) {
648 SERIALIZE_STR(q->alias);
649 }
650 p++;
651 }
652 }
653
654 if (ce->trait_precedences) {
655 zend_trait_precedence **p, *q;
656
657 SERIALIZE_PTR(ce->trait_precedences);
658 p = ce->trait_precedences;
659 UNSERIALIZE_PTR(p);
660
661 while (*p) {
662 SERIALIZE_PTR(*p);
663 q = *p;
664 UNSERIALIZE_PTR(q);
665
666 if (q->trait_method) {
667 zend_trait_method_reference *m;
668
669 SERIALIZE_PTR(q->trait_method);
670 m = q->trait_method;
671 UNSERIALIZE_PTR(m);
672
673 if (m->method_name) {
674 SERIALIZE_STR(m->method_name);
675 }
676 if (m->class_name) {
677 SERIALIZE_STR(m->class_name);
678 }
679 }
680
681 if (q->exclude_from_classes) {
682 zend_string **s;
683
684 SERIALIZE_PTR(q->exclude_from_classes);
685 s = (zend_string**)q->exclude_from_classes;
686 UNSERIALIZE_PTR(s);
687
688 while (*s) {
689 SERIALIZE_STR(*s);
690 s++;
691 }
692 }
693 p++;
694 }
695 }
696
697 SERIALIZE_PTR(ce->parent);
698 SERIALIZE_PTR(ce->constructor);
699 SERIALIZE_PTR(ce->destructor);
700 SERIALIZE_PTR(ce->clone);
701 SERIALIZE_PTR(ce->__get);
702 SERIALIZE_PTR(ce->__set);
703 SERIALIZE_PTR(ce->__call);
704 SERIALIZE_PTR(ce->serialize_func);
705 SERIALIZE_PTR(ce->unserialize_func);
706 SERIALIZE_PTR(ce->__isset);
707 SERIALIZE_PTR(ce->__unset);
708 SERIALIZE_PTR(ce->__tostring);
709 SERIALIZE_PTR(ce->__callstatic);
710 SERIALIZE_PTR(ce->__debugInfo);
711 }
712
713 static void zend_file_cache_serialize(zend_persistent_script *script,
714 zend_file_cache_metainfo *info,
715 void *buf)
716 {
717 zend_persistent_script *new_script;
718
719 memcpy(info->magic, "OPCACHE", 8);
720 memcpy(info->system_id, ZCG(system_id), 32);
721 info->mem_size = script->size;
722 info->str_size = 0;
723 info->script_offset = (char*)script - (char*)script->mem;
724 info->timestamp = script->timestamp;
725
726 memcpy(buf, script->mem, script->size);
727
728 new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
729 SERIALIZE_STR(new_script->script.filename);
730
731 zend_file_cache_serialize_hash(&new_script->script.class_table, script, info, buf, zend_file_cache_serialize_class);
732 zend_file_cache_serialize_hash(&new_script->script.function_table, script, info, buf, zend_file_cache_serialize_func);
733 zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf);
734
735 SERIALIZE_PTR(new_script->arena_mem);
736 new_script->mem = NULL;
737 }
738
739 static char *zend_file_cache_get_bin_file_path(zend_string *script_path)
740 {
741 size_t len;
742 char *filename;
743
744 #ifndef ZEND_WIN32
745 len = strlen(ZCG(accel_directives).file_cache);
746 filename = emalloc(len + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
747 memcpy(filename, ZCG(accel_directives).file_cache, len);
748 filename[len] = '/';
749 memcpy(filename + len + 1, ZCG(system_id), 32);
750 memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
751 memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
752 #else
753 PHP_MD5_CTX ctx;
754 char md5uname[32];
755 unsigned char digest[16], c;
756 size_t i;
757 char *uname = php_win32_get_username();
758
759 PHP_MD5Init(&ctx);
760 PHP_MD5Update(&ctx, uname, strlen(uname));
761 PHP_MD5Final(digest, &ctx);
762 for (i = 0; i < 16; i++) {
763 c = digest[i] >> 4;
764 c = (c <= 9) ? c + '0' : c - 10 + 'a';
765 md5uname[i * 2] = c;
766 c = digest[i] & 0x0f;
767 c = (c <= 9) ? c + '0' : c - 10 + 'a';
768 md5uname[(i * 2) + 1] = c;
769 }
770
771 len = strlen(ZCG(accel_directives).file_cache);
772
773 filename = emalloc(len + 33 + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
774
775 memcpy(filename, ZCG(accel_directives).file_cache, len);
776 filename[len] = '\\';
777 memcpy(filename + 1 + len, md5uname, 32);
778 len += 1 + 32;
779 filename[len] = '\\';
780
781 memcpy(filename + len + 1, ZCG(system_id), 32);
782
783 if (ZSTR_LEN(script_path) >= 7 && ':' == ZSTR_VAL(script_path)[4] && '/' == ZSTR_VAL(script_path)[5] && '/' == ZSTR_VAL(script_path)[6]) {
784 /* phar:// or file:// */
785 *(filename + len + 33) = '\\';
786 memcpy(filename + len + 34, ZSTR_VAL(script_path), 4);
787 if (ZSTR_LEN(script_path) - 7 >= 2 && ':' == ZSTR_VAL(script_path)[8]) {
788 *(filename + len + 38) = '\\';
789 *(filename + len + 39) = ZSTR_VAL(script_path)[7];
790 memcpy(filename + len + 40, ZSTR_VAL(script_path) + 9, ZSTR_LEN(script_path) - 9);
791 memcpy(filename + len + 40 + ZSTR_LEN(script_path) - 9, SUFFIX, sizeof(SUFFIX));
792 } else {
793 memcpy(filename + len + 38, ZSTR_VAL(script_path) + 7, ZSTR_LEN(script_path) - 7);
794 memcpy(filename + len + 38 + ZSTR_LEN(script_path) - 7, SUFFIX, sizeof(SUFFIX));
795 }
796 } else if (ZSTR_LEN(script_path) >= 2 && ':' == ZSTR_VAL(script_path)[1]) {
797 /* local fs */
798 *(filename + len + 33) = '\\';
799 *(filename + len + 34) = ZSTR_VAL(script_path)[0];
800 memcpy(filename + len + 35, ZSTR_VAL(script_path) + 2, ZSTR_LEN(script_path) - 2);
801 memcpy(filename + len + 35 + ZSTR_LEN(script_path) - 2, SUFFIX, sizeof(SUFFIX));
802 } else {
803 /* network path */
804 memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
805 memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
806 }
807 free(uname);
808 #endif
809
810 return filename;
811 }
812
813 int zend_file_cache_script_store(zend_persistent_script *script, int in_shm)
814 {
815 int fd;
816 char *filename;
817 zend_file_cache_metainfo info;
818 #ifdef HAVE_SYS_UIO_H
819 struct iovec vec[3];
820 #endif
821 void *mem, *buf;
822
823 filename = zend_file_cache_get_bin_file_path(script->script.filename);
824
825 if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {
826 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s'\n", filename);
827 efree(filename);
828 return FAILURE;
829 }
830
831 #ifndef ZEND_WIN32
832 fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
833 #else
834 fd = php_win32_ioutil_open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, _S_IREAD | _S_IWRITE);
835 #endif
836 if (fd < 0) {
837 if (errno != EEXIST) {
838 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s'\n", filename);
839 }
840 efree(filename);
841 return FAILURE;
842 }
843
844 if (zend_file_cache_flock(fd, LOCK_EX) != 0) {
845 close(fd);
846 efree(filename);
847 return FAILURE;
848 }
849
850 #ifdef __SSE2__
851 /* Align to 64-byte boundary */
852 mem = emalloc(script->size + 64);
853 buf = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
854 #else
855 mem = buf = emalloc(script->size);
856 #endif
857
858 ZCG(mem) = zend_string_alloc(4096 - (_ZSTR_HEADER_SIZE + 1), 0);
859
860 zend_shared_alloc_init_xlat_table();
861 if (!in_shm) {
862 script->corrupted = 1; /* used to check if script restored to SHM or process memory */
863 }
864 zend_file_cache_serialize(script, &info, buf);
865 if (!in_shm) {
866 script->corrupted = 0;
867 }
868 zend_shared_alloc_destroy_xlat_table();
869
870 info.checksum = zend_adler32(ADLER32_INIT, buf, script->size);
871 info.checksum = zend_adler32(info.checksum, (unsigned char*)ZSTR_VAL((zend_string*)ZCG(mem)), info.str_size);
872
873 #ifdef HAVE_SYS_UIO_H
874 vec[0].iov_base = &info;
875 vec[0].iov_len = sizeof(info);
876 vec[1].iov_base = buf;
877 vec[1].iov_len = script->size;
878 vec[2].iov_base = ZSTR_VAL((zend_string*)ZCG(mem));
879 vec[2].iov_len = info.str_size;
880
881 if (writev(fd, vec, 3) != (ssize_t)(sizeof(info) + script->size + info.str_size)) {
882 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
883 zend_string_release((zend_string*)ZCG(mem));
884 close(fd);
885 efree(mem);
886 unlink(filename);
887 efree(filename);
888 return FAILURE;
889 }
890 #else
891 if (ZEND_LONG_MAX < (zend_long)(sizeof(info) + script->size + info.str_size) ||
892 write(fd, &info, sizeof(info)) != sizeof(info) ||
893 write(fd, buf, script->size) != script->size ||
894 write(fd, ((zend_string*)ZCG(mem))->val, info.str_size) != info.str_size
895 ) {
896 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
897 zend_string_release((zend_string*)ZCG(mem));
898 close(fd);
899 efree(mem);
900 unlink(filename);
901 efree(filename);
902 return FAILURE;
903 }
904 #endif
905
906 zend_string_release((zend_string*)ZCG(mem));
907 efree(mem);
908 if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
909 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
910 }
911 close(fd);
912 efree(filename);
913
914 return SUCCESS;
915 }
916
917 static void zend_file_cache_unserialize_hash(HashTable *ht,
918 zend_persistent_script *script,
919 void *buf,
920 unserialize_callback_t func,
921 dtor_func_t dtor)
922 {
923 Bucket *p, *end;
924
925 ht->pDestructor = dtor;
926 if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
927 if (EXPECTED(!file_cache_only)) {
928 HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
929 } else {
930 HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
931 }
932 return;
933 }
934 if (IS_UNSERIALIZED(ht->arData)) {
935 return;
936 }
937 UNSERIALIZE_PTR(ht->arData);
938 p = ht->arData;
939 end = p + ht->nNumUsed;
940 while (p < end) {
941 if (Z_TYPE(p->val) != IS_UNDEF) {
942 UNSERIALIZE_STR(p->key);
943 func(&p->val, script, buf);
944 }
945 p++;
946 }
947 }
948
949 static zend_ast *zend_file_cache_unserialize_ast(zend_ast *ast,
950 zend_persistent_script *script,
951 void *buf)
952 {
953 uint32_t i;
954
955 UNSERIALIZE_PTR(ast);
956
957 if (ast->kind == ZEND_AST_ZVAL) {
958 zend_file_cache_unserialize_zval(&((zend_ast_zval*)ast)->val, script, buf);
959 } else if (zend_ast_is_list(ast)) {
960 zend_ast_list *list = zend_ast_get_list(ast);
961 for (i = 0; i < list->children; i++) {
962 if (list->child[i]) {
963 list->child[i] = zend_file_cache_unserialize_ast(list->child[i], script, buf);
964 }
965 }
966 } else {
967 uint32_t children = zend_ast_get_num_children(ast);
968 for (i = 0; i < children; i++) {
969 if (ast->child[i]) {
970 ast->child[i] = zend_file_cache_unserialize_ast(ast->child[i], script, buf);
971 }
972 }
973 }
974 return ast;
975 }
976
977 static void zend_file_cache_unserialize_zval(zval *zv,
978 zend_persistent_script *script,
979 void *buf)
980 {
981 switch (Z_TYPE_P(zv)) {
982 case IS_STRING:
983 case IS_CONSTANT:
984 if (!IS_UNSERIALIZED(Z_STR_P(zv))) {
985 UNSERIALIZE_STR(Z_STR_P(zv));
986 }
987 break;
988 case IS_ARRAY:
989 if (!IS_UNSERIALIZED(Z_ARR_P(zv))) {
990 HashTable *ht;
991
992 UNSERIALIZE_PTR(Z_ARR_P(zv));
993 ht = Z_ARR_P(zv);
994 zend_file_cache_unserialize_hash(ht,
995 script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
996 }
997 break;
998 case IS_REFERENCE:
999 if (!IS_UNSERIALIZED(Z_REF_P(zv))) {
1000 zend_reference *ref;
1001
1002 UNSERIALIZE_PTR(Z_REF_P(zv));
1003 ref = Z_REF_P(zv);
1004 zend_file_cache_unserialize_zval(&ref->val, script, buf);
1005 }
1006 break;
1007 case IS_CONSTANT_AST:
1008 if (!IS_UNSERIALIZED(Z_AST_P(zv))) {
1009 zend_ast_ref *ast;
1010
1011 UNSERIALIZE_PTR(Z_AST_P(zv));
1012 ast = Z_AST_P(zv);
1013 if (!IS_UNSERIALIZED(ast->ast)) {
1014 ast->ast = zend_file_cache_unserialize_ast(ast->ast, script, buf);
1015 }
1016 }
1017 break;
1018 }
1019 }
1020
1021 static void zend_file_cache_unserialize_op_array(zend_op_array *op_array,
1022 zend_persistent_script *script,
1023 void *buf)
1024 {
1025 if (op_array->static_variables && !IS_UNSERIALIZED(op_array->static_variables)) {
1026 HashTable *ht;
1027
1028 UNSERIALIZE_PTR(op_array->static_variables);
1029 ht = op_array->static_variables;
1030 zend_file_cache_unserialize_hash(ht,
1031 script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
1032 }
1033
1034 if (op_array->refcount) {
1035 op_array->refcount = NULL;
1036 UNSERIALIZE_PTR(op_array->literals);
1037 UNSERIALIZE_PTR(op_array->opcodes);
1038 UNSERIALIZE_PTR(op_array->arg_info);
1039 UNSERIALIZE_PTR(op_array->vars);
1040 UNSERIALIZE_STR(op_array->function_name);
1041 UNSERIALIZE_STR(op_array->filename);
1042 UNSERIALIZE_PTR(op_array->live_range);
1043 UNSERIALIZE_PTR(op_array->scope);
1044 UNSERIALIZE_STR(op_array->doc_comment);
1045 UNSERIALIZE_PTR(op_array->try_catch_array);
1046 UNSERIALIZE_PTR(op_array->prototype);
1047 return;
1048 }
1049
1050 if (op_array->literals && !IS_UNSERIALIZED(op_array->literals)) {
1051 zval *p, *end;
1052
1053 UNSERIALIZE_PTR(op_array->literals);
1054 p = op_array->literals;
1055 end = p + op_array->last_literal;
1056 while (p < end) {
1057 zend_file_cache_unserialize_zval(p, script, buf);
1058 p++;
1059 }
1060 }
1061
1062 if (!IS_UNSERIALIZED(op_array->opcodes)) {
1063 zend_op *opline, *end;
1064
1065 UNSERIALIZE_PTR(op_array->opcodes);
1066 opline = op_array->opcodes;
1067 end = opline + op_array->last;
1068 while (opline < end) {
1069 # if ZEND_USE_ABS_CONST_ADDR
1070 if (opline->op1_type == IS_CONST) {
1071 UNSERIALIZE_PTR(opline->op1.zv);
1072 }
1073 if (opline->op2_type == IS_CONST) {
1074 UNSERIALIZE_PTR(opline->op2.zv);
1075 }
1076 # endif
1077 # if ZEND_USE_ABS_JMP_ADDR
1078 switch (opline->opcode) {
1079 case ZEND_JMP:
1080 case ZEND_FAST_CALL:
1081 UNSERIALIZE_PTR(opline->op1.jmp_addr);
1082 break;
1083 case ZEND_JMPZNZ:
1084 /* relative extended_value don't have to be changed */
1085 /* break omitted intentionally */
1086 case ZEND_JMPZ:
1087 case ZEND_JMPNZ:
1088 case ZEND_JMPZ_EX:
1089 case ZEND_JMPNZ_EX:
1090 case ZEND_JMP_SET:
1091 case ZEND_COALESCE:
1092 case ZEND_FE_RESET_R:
1093 case ZEND_FE_RESET_RW:
1094 case ZEND_ASSERT_CHECK:
1095 UNSERIALIZE_PTR(opline->op2.jmp_addr);
1096 break;
1097 case ZEND_DECLARE_ANON_CLASS:
1098 case ZEND_DECLARE_ANON_INHERITED_CLASS:
1099 case ZEND_FE_FETCH_R:
1100 case ZEND_FE_FETCH_RW:
1101 case ZEND_SWITCH_LONG:
1102 case ZEND_SWITCH_STRING:
1103 /* relative extended_value don't have to be changed */
1104 break;
1105 }
1106 # endif
1107 zend_deserialize_opcode_handler(opline);
1108 opline++;
1109 }
1110
1111 if (op_array->arg_info) {
1112 zend_arg_info *p, *end;
1113 UNSERIALIZE_PTR(op_array->arg_info);
1114 p = op_array->arg_info;
1115 end = p + op_array->num_args;
1116 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1117 p--;
1118 }
1119 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
1120 end++;
1121 }
1122 while (p < end) {
1123 if (!IS_UNSERIALIZED(p->name)) {
1124 UNSERIALIZE_STR(p->name);
1125 }
1126 if (p->type & (Z_UL(1) << (sizeof(zend_type)*8-1))) { /* type is class */
1127 zend_bool allow_null = (p->type & (Z_UL(1) << (sizeof(zend_type)*8-2))) != 0; /* type allow null */
1128 zend_string *type_name = (zend_string*)(p->type & ~(((Z_UL(1) << (sizeof(zend_type)*8-1))) | ((Z_UL(1) << (sizeof(zend_type)*8-2)))));
1129
1130 UNSERIALIZE_STR(type_name);
1131 p->type = ZEND_TYPE_ENCODE_CLASS(type_name, allow_null);
1132 }
1133 p++;
1134 }
1135 }
1136
1137 if (op_array->vars) {
1138 zend_string **p, **end;
1139
1140 UNSERIALIZE_PTR(op_array->vars);
1141 p = op_array->vars;
1142 end = p + op_array->last_var;
1143 while (p < end) {
1144 if (!IS_UNSERIALIZED(*p)) {
1145 UNSERIALIZE_STR(*p);
1146 }
1147 p++;
1148 }
1149 }
1150
1151 UNSERIALIZE_STR(op_array->function_name);
1152 UNSERIALIZE_STR(op_array->filename);
1153 UNSERIALIZE_PTR(op_array->live_range);
1154 UNSERIALIZE_PTR(op_array->scope);
1155 UNSERIALIZE_STR(op_array->doc_comment);
1156 UNSERIALIZE_PTR(op_array->try_catch_array);
1157 UNSERIALIZE_PTR(op_array->prototype);
1158 }
1159 }
1160
1161 static void zend_file_cache_unserialize_func(zval *zv,
1162 zend_persistent_script *script,
1163 void *buf)
1164 {
1165 zend_op_array *op_array;
1166
1167 UNSERIALIZE_PTR(Z_PTR_P(zv));
1168 op_array = Z_PTR_P(zv);
1169 zend_file_cache_unserialize_op_array(op_array, script, buf);
1170 }
1171
1172 static void zend_file_cache_unserialize_prop_info(zval *zv,
1173 zend_persistent_script *script,
1174 void *buf)
1175 {
1176 if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
1177 zend_property_info *prop;
1178
1179 UNSERIALIZE_PTR(Z_PTR_P(zv));
1180 prop = Z_PTR_P(zv);
1181
1182 ZEND_ASSERT(prop->ce != NULL && prop->name != NULL);
1183 if (!IS_UNSERIALIZED(prop->ce)) {
1184 UNSERIALIZE_PTR(prop->ce);
1185 UNSERIALIZE_STR(prop->name);
1186 if (prop->doc_comment) {
1187 UNSERIALIZE_STR(prop->doc_comment);
1188 }
1189 }
1190 }
1191 }
1192
1193 static void zend_file_cache_unserialize_class_constant(zval *zv,
1194 zend_persistent_script *script,
1195 void *buf)
1196 {
1197 if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
1198 zend_class_constant *c;
1199
1200 UNSERIALIZE_PTR(Z_PTR_P(zv));
1201 c = Z_PTR_P(zv);
1202
1203 ZEND_ASSERT(c->ce != NULL);
1204 if (!IS_UNSERIALIZED(c->ce)) {
1205 UNSERIALIZE_PTR(c->ce);
1206
1207 zend_file_cache_unserialize_zval(&c->value, script, buf);
1208
1209 if (c->doc_comment) {
1210 UNSERIALIZE_STR(c->doc_comment);
1211 }
1212 }
1213 }
1214 }
1215
1216 static void zend_file_cache_unserialize_class(zval *zv,
1217 zend_persistent_script *script,
1218 void *buf)
1219 {
1220 zend_class_entry *ce;
1221
1222 UNSERIALIZE_PTR(Z_PTR_P(zv));
1223 ce = Z_PTR_P(zv);
1224
1225 UNSERIALIZE_STR(ce->name);
1226 zend_file_cache_unserialize_hash(&ce->function_table,
1227 script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
1228 if (ce->default_properties_table) {
1229 zval *p, *end;
1230
1231 UNSERIALIZE_PTR(ce->default_properties_table);
1232 p = ce->default_properties_table;
1233 end = p + ce->default_properties_count;
1234 while (p < end) {
1235 zend_file_cache_unserialize_zval(p, script, buf);
1236 p++;
1237 }
1238 }
1239 if (ce->default_static_members_table) {
1240 zval *p, *end;
1241
1242 UNSERIALIZE_PTR(ce->default_static_members_table);
1243 p = ce->default_static_members_table;
1244 end = p + ce->default_static_members_count;
1245 while (p < end) {
1246 zend_file_cache_unserialize_zval(p, script, buf);
1247 p++;
1248 }
1249 }
1250 zend_file_cache_unserialize_hash(&ce->constants_table,
1251 script, buf, zend_file_cache_unserialize_class_constant, NULL);
1252 UNSERIALIZE_STR(ce->info.user.filename);
1253 UNSERIALIZE_STR(ce->info.user.doc_comment);
1254 zend_file_cache_unserialize_hash(&ce->properties_info,
1255 script, buf, zend_file_cache_unserialize_prop_info, NULL);
1256
1257 if (ce->trait_aliases) {
1258 zend_trait_alias **p, *q;
1259
1260 UNSERIALIZE_PTR(ce->trait_aliases);
1261 p = ce->trait_aliases;
1262
1263 while (*p) {
1264 UNSERIALIZE_PTR(*p);
1265 q = *p;
1266
1267 if (q->trait_method) {
1268 zend_trait_method_reference *m;
1269
1270 UNSERIALIZE_PTR(q->trait_method);
1271 m = q->trait_method;
1272
1273 if (m->method_name) {
1274 UNSERIALIZE_STR(m->method_name);
1275 }
1276 if (m->class_name) {
1277 UNSERIALIZE_STR(m->class_name);
1278 }
1279 }
1280
1281 if (q->alias) {
1282 UNSERIALIZE_STR(q->alias);
1283 }
1284 p++;
1285 }
1286 }
1287
1288 if (ce->trait_precedences) {
1289 zend_trait_precedence **p, *q;
1290
1291 UNSERIALIZE_PTR(ce->trait_precedences);
1292 p = ce->trait_precedences;
1293
1294 while (*p) {
1295 UNSERIALIZE_PTR(*p);
1296 q = *p;
1297
1298 if (q->trait_method) {
1299 zend_trait_method_reference *m;
1300
1301 UNSERIALIZE_PTR(q->trait_method);
1302 m = q->trait_method;
1303
1304 if (m->method_name) {
1305 UNSERIALIZE_STR(m->method_name);
1306 }
1307 if (m->class_name) {
1308 UNSERIALIZE_STR(m->class_name);
1309 }
1310 }
1311
1312 if (q->exclude_from_classes) {
1313 zend_string **s;
1314
1315 UNSERIALIZE_PTR(q->exclude_from_classes);
1316 s = (zend_string**)q->exclude_from_classes;
1317
1318 while (*s) {
1319 UNSERIALIZE_STR(*s);
1320 s++;
1321 }
1322 }
1323 p++;
1324 }
1325 }
1326
1327 UNSERIALIZE_PTR(ce->parent);
1328 UNSERIALIZE_PTR(ce->constructor);
1329 UNSERIALIZE_PTR(ce->destructor);
1330 UNSERIALIZE_PTR(ce->clone);
1331 UNSERIALIZE_PTR(ce->__get);
1332 UNSERIALIZE_PTR(ce->__set);
1333 UNSERIALIZE_PTR(ce->__call);
1334 UNSERIALIZE_PTR(ce->serialize_func);
1335 UNSERIALIZE_PTR(ce->unserialize_func);
1336 UNSERIALIZE_PTR(ce->__isset);
1337 UNSERIALIZE_PTR(ce->__unset);
1338 UNSERIALIZE_PTR(ce->__tostring);
1339 UNSERIALIZE_PTR(ce->__callstatic);
1340 UNSERIALIZE_PTR(ce->__debugInfo);
1341
1342 if (UNEXPECTED((ce->ce_flags & ZEND_ACC_ANON_CLASS))) {
1343 ce->serialize = zend_class_serialize_deny;
1344 ce->unserialize = zend_class_unserialize_deny;
1345 }
1346 }
1347
1348 static void zend_file_cache_unserialize(zend_persistent_script *script,
1349 void *buf)
1350 {
1351 script->mem = buf;
1352
1353 UNSERIALIZE_STR(script->script.filename);
1354
1355 zend_file_cache_unserialize_hash(&script->script.class_table,
1356 script, buf, zend_file_cache_unserialize_class, ZEND_CLASS_DTOR);
1357 zend_file_cache_unserialize_hash(&script->script.function_table,
1358 script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
1359 zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf);
1360
1361 UNSERIALIZE_PTR(script->arena_mem);
1362 }
1363
1364 zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
1365 {
1366 zend_string *full_path = file_handle->opened_path;
1367 int fd;
1368 char *filename;
1369 zend_persistent_script *script;
1370 zend_file_cache_metainfo info;
1371 zend_accel_hash_entry *bucket;
1372 void *mem, *checkpoint, *buf;
1373 int cache_it = 1;
1374 unsigned int actual_checksum;
1375 int ok;
1376
1377 if (!full_path) {
1378 return NULL;
1379 }
1380 filename = zend_file_cache_get_bin_file_path(full_path);
1381
1382 #ifndef ZEND_WIN32
1383 fd = open(filename, O_RDONLY | O_BINARY);
1384 #else
1385 fd = php_win32_ioutil_open(filename, O_RDONLY | O_BINARY);
1386 #endif
1387 if (fd < 0) {
1388 efree(filename);
1389 return NULL;
1390 }
1391
1392 if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
1393 close(fd);
1394 efree(filename);
1395 return NULL;
1396 }
1397
1398 if (read(fd, &info, sizeof(info)) != sizeof(info)) {
1399 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
1400 zend_file_cache_flock(fd, LOCK_UN);
1401 close(fd);
1402 unlink(filename);
1403 efree(filename);
1404 return NULL;
1405 }
1406
1407 /* verify header */
1408 if (memcmp(info.magic, "OPCACHE", 8) != 0) {
1409 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename);
1410 zend_file_cache_flock(fd, LOCK_UN);
1411 close(fd);
1412 unlink(filename);
1413 efree(filename);
1414 return NULL;
1415 }
1416 if (memcmp(info.system_id, ZCG(system_id), 32) != 0) {
1417 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename);
1418 zend_file_cache_flock(fd, LOCK_UN);
1419 close(fd);
1420 unlink(filename);
1421 efree(filename);
1422 return NULL;
1423 }
1424
1425 /* verify timestamp */
1426 if (ZCG(accel_directives).validate_timestamps &&
1427 zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
1428 if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1429 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
1430 }
1431 close(fd);
1432 unlink(filename);
1433 efree(filename);
1434 return NULL;
1435 }
1436
1437 checkpoint = zend_arena_checkpoint(CG(arena));
1438 #ifdef __SSE2__
1439 /* Align to 64-byte boundary */
1440 mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
1441 mem = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
1442 #else
1443 mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
1444 #endif
1445
1446 if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
1447 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
1448 zend_file_cache_flock(fd, LOCK_UN);
1449 close(fd);
1450 unlink(filename);
1451 zend_arena_release(&CG(arena), checkpoint);
1452 efree(filename);
1453 return NULL;
1454 }
1455 if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1456 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
1457 }
1458 close(fd);
1459
1460 /* verify checksum */
1461 if (ZCG(accel_directives).file_cache_consistency_checks &&
1462 (actual_checksum = zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size)) != info.checksum) {
1463 zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s' excepted checksum: 0x%08x actual checksum: 0x%08x\n", filename, info.checksum, actual_checksum);
1464 unlink(filename);
1465 zend_arena_release(&CG(arena), checkpoint);
1466 efree(filename);
1467 return NULL;
1468 }
1469
1470 if (!file_cache_only &&
1471 !ZCSG(restart_in_progress) &&
1472 !ZSMMG(memory_exhausted) &&
1473 accelerator_shm_read_lock() == SUCCESS) {
1474 /* exclusive lock */
1475 zend_shared_alloc_lock();
1476
1477 /* Check if we still need to put the file into the cache (may be it was
1478 * already stored by another process. This final check is done under
1479 * exclusive lock) */
1480 bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
1481 if (bucket) {
1482 script = (zend_persistent_script *)bucket->data;
1483 if (!script->corrupted) {
1484 zend_shared_alloc_unlock();
1485 zend_arena_release(&CG(arena), checkpoint);
1486 efree(filename);
1487 return script;
1488 }
1489 }
1490
1491 if (zend_accel_hash_is_full(&ZCSG(hash))) {
1492 zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1493 ZSMMG(memory_exhausted) = 1;
1494 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1495 zend_shared_alloc_unlock();
1496 goto use_process_mem;
1497 }
1498
1499 #ifdef __SSE2__
1500 /* Align to 64-byte boundary */
1501 buf = zend_shared_alloc(info.mem_size + 64);
1502 buf = (void*)(((zend_uintptr_t)buf + 63L) & ~63L);
1503 #else
1504 buf = zend_shared_alloc(info.mem_size);
1505 #endif
1506
1507 if (!buf) {
1508 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1509 zend_shared_alloc_unlock();
1510 goto use_process_mem;
1511 }
1512 memcpy(buf, mem, info.mem_size);
1513 } else {
1514 use_process_mem:
1515 buf = mem;
1516 cache_it = 0;
1517 }
1518
1519 ZCG(mem) = ((char*)mem + info.mem_size);
1520 script = (zend_persistent_script*)((char*)buf + info.script_offset);
1521 script->corrupted = !cache_it; /* used to check if script restored to SHM or process memory */
1522
1523 ok = 1;
1524 zend_try {
1525 zend_file_cache_unserialize(script, buf);
1526 } zend_catch {
1527 ok = 0;
1528 } zend_end_try();
1529 if (!ok) {
1530 if (cache_it) {
1531 zend_shared_alloc_unlock();
1532 goto use_process_mem;
1533 } else {
1534 zend_arena_release(&CG(arena), checkpoint);
1535 efree(filename);
1536 return NULL;
1537 }
1538 }
1539
1540 script->corrupted = 0;
1541
1542 if (cache_it) {
1543 script->dynamic_members.checksum = zend_accel_script_checksum(script);
1544 script->dynamic_members.last_used = ZCG(request_time);
1545
1546 zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->script.filename), ZSTR_LEN(script->script.filename), 0, script);
1547
1548 zend_shared_alloc_unlock();
1549 zend_accel_error(ACCEL_LOG_INFO, "File cached script loaded into memory '%s'", ZSTR_VAL(script->script.filename));
1550
1551 zend_arena_release(&CG(arena), checkpoint);
1552 }
1553 efree(filename);
1554
1555 return script;
1556 }
1557
1558 void zend_file_cache_invalidate(zend_string *full_path)
1559 {
1560 char *filename;
1561
1562 filename = zend_file_cache_get_bin_file_path(full_path);
1563
1564 unlink(filename);
1565 efree(filename);
1566 }
1567
1568 #endif /* HAVE_OPCACHE_FILE_CACHE */
1569