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