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