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