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