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