xref: /PHP-8.4/ext/phar/tar.c (revision 72c02229)
1 /*
2   +----------------------------------------------------------------------+
3   | TAR archive support for Phar                                         |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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   | https://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   |          Gregory Beaver <cellog@php.net>                             |
17   +----------------------------------------------------------------------+
18 */
19 
20 #include "phar_internal.h"
21 #include "ext/standard/php_string.h" /* For php_stristr() */
22 
phar_tar_number(const char * buf,size_t len)23 static uint32_t phar_tar_number(const char *buf, size_t len) /* {{{ */
24 {
25 	uint32_t num = 0;
26 	size_t i = 0;
27 
28 	while (i < len && buf[i] == ' ') {
29 		++i;
30 	}
31 
32 	while (i < len && buf[i] >= '0' && buf[i] <= '7') {
33 		num = num * 8 + (buf[i] - '0');
34 		++i;
35 	}
36 
37 	return num;
38 }
39 /* }}} */
40 
41 /* adapted from format_octal() in libarchive
42  *
43  * Copyright (c) 2003-2009 Tim Kientzle
44  * All rights reserved.
45  *
46  * Redistribution and use in source and binary forms, with or without
47  * modification, are permitted provided that the following conditions
48  * are met:
49  * 1. Redistributions of source code must retain the above copyright
50  *    notice, this list of conditions and the following disclaimer.
51  * 2. Redistributions in binary form must reproduce the above copyright
52  *    notice, this list of conditions and the following disclaimer in the
53  *    documentation and/or other materials provided with the distribution.
54  *
55  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
56  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
57  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
58  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
59  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
60  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
61  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
62  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
64  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65  */
phar_tar_octal(char * buf,uint32_t val,size_t len)66 static zend_result phar_tar_octal(char *buf, uint32_t val, size_t len) /* {{{ */
67 {
68 	char *p = buf;
69 	size_t s = len;
70 
71 	p += len;		/* Start at the end and work backwards. */
72 	while (s-- > 0) {
73 		*--p = (char)('0' + (val & 7));
74 		val >>= 3;
75 	}
76 
77 	if (val == 0) {
78 		return SUCCESS;
79 	}
80 
81 	/* If it overflowed, fill field with max value. */
82 	while (len-- > 0) {
83 		*p++ = '7';
84 	}
85 
86 	return FAILURE;
87 }
88 /* }}} */
89 
phar_tar_checksum(char * buf,size_t len)90 static uint32_t phar_tar_checksum(char *buf, size_t len) /* {{{ */
91 {
92 	uint32_t sum = 0;
93 	char *end = buf + len;
94 
95 	while (buf != end) {
96 		sum += (unsigned char)*buf;
97 		++buf;
98 	}
99 	return sum;
100 }
101 /* }}} */
102 
phar_is_tar(char * buf,char * fname)103 bool phar_is_tar(char *buf, char *fname) /* {{{ */
104 {
105 	tar_header *header = (tar_header *) buf;
106 	uint32_t checksum = phar_tar_number(header->checksum, sizeof(header->checksum));
107 	bool is_tar;
108 	char save[sizeof(header->checksum)], *bname;
109 
110 	/* assume that the first filename in a tar won't begin with <?php */
111 	if (!strncmp(buf, "<?php", sizeof("<?php")-1)) {
112 		return false;
113 	}
114 
115 	memcpy(save, header->checksum, sizeof(header->checksum));
116 	memset(header->checksum, ' ', sizeof(header->checksum));
117 	is_tar = (checksum == phar_tar_checksum(buf, 512));
118 	memcpy(header->checksum, save, sizeof(header->checksum));
119 	if ((bname = strrchr(fname, PHP_DIR_SEPARATOR))) {
120 		fname = bname;
121 	}
122 	if (!is_tar && (bname = strstr(fname, ".tar")) && (bname[4] == '\0' || bname[4] == '.')) {
123 		/* probably a corrupted tar - so we will pretend it is one */
124 		return true;
125 	}
126 	return is_tar;
127 }
128 /* }}} */
129 
phar_open_or_create_tar(char * fname,size_t fname_len,char * alias,size_t alias_len,int is_data,uint32_t options,phar_archive_data ** pphar,char ** error)130 zend_result phar_open_or_create_tar(char *fname, size_t fname_len, char *alias, size_t alias_len, int is_data, uint32_t options, phar_archive_data** pphar, char **error) /* {{{ */
131 {
132 	phar_archive_data *phar;
133 	zend_result ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error);
134 
135 	if (FAILURE == ret) {
136 		return FAILURE;
137 	}
138 
139 	if (pphar) {
140 		*pphar = phar;
141 	}
142 
143 	phar->is_data = is_data;
144 
145 	if (phar->is_tar) {
146 		return ret;
147 	}
148 
149 	if (phar->is_brandnew) {
150 		phar->is_tar = 1;
151 		phar->is_zip = 0;
152 		return SUCCESS;
153 	}
154 
155 	/* we've reached here - the phar exists and is a regular phar */
156 	if (error) {
157 		spprintf(error, 4096, "phar tar error: \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a tar-based phar", fname);
158 	}
159 	return FAILURE;
160 }
161 /* }}} */
162 
phar_tar_process_metadata(phar_entry_info * entry,php_stream * fp)163 static zend_result phar_tar_process_metadata(phar_entry_info *entry, php_stream *fp) /* {{{ */
164 {
165 	char *metadata;
166 	size_t save = php_stream_tell(fp), read;
167 	phar_entry_info *mentry;
168 
169 	metadata = (char *) safe_emalloc(1, entry->uncompressed_filesize, 1);
170 
171 	read = php_stream_read(fp, metadata, entry->uncompressed_filesize);
172 	if (read != entry->uncompressed_filesize) {
173 		efree(metadata);
174 		php_stream_seek(fp, save, SEEK_SET);
175 		return FAILURE;
176 	}
177 
178 	phar_parse_metadata_lazy(metadata, &entry->metadata_tracker, entry->uncompressed_filesize, entry->is_persistent);
179 
180 	if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
181 		if (phar_metadata_tracker_has_data(&entry->phar->metadata_tracker, entry->phar->is_persistent)) {
182 			efree(metadata);
183 			return FAILURE;
184 		}
185 		entry->phar->metadata_tracker = entry->metadata_tracker;
186 		entry->metadata_tracker.str = NULL;
187 		ZVAL_UNDEF(&entry->metadata_tracker.val);
188 	} else if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && NULL != (mentry = zend_hash_str_find_ptr(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1)))) {
189 		if (phar_metadata_tracker_has_data(&mentry->metadata_tracker, mentry->is_persistent)) {
190 			efree(metadata);
191 			return FAILURE;
192 		}
193 		/* transfer this metadata to the entry it refers */
194 		mentry->metadata_tracker = entry->metadata_tracker;
195 		entry->metadata_tracker.str = NULL;
196 		ZVAL_UNDEF(&entry->metadata_tracker.val);
197 	}
198 
199 	efree(metadata);
200 	php_stream_seek(fp, save, SEEK_SET);
201 	return SUCCESS;
202 }
203 /* }}} */
204 
phar_parse_tarfile(php_stream * fp,char * fname,size_t fname_len,char * alias,size_t alias_len,phar_archive_data ** pphar,uint32_t compression,char ** error)205 zend_result phar_parse_tarfile(php_stream* fp, char *fname, size_t fname_len, char *alias, size_t alias_len, phar_archive_data** pphar, uint32_t compression, char **error) /* {{{ */
206 {
207 	char buf[512], *actual_alias = NULL, *p;
208 	phar_entry_info entry = {0};
209 	size_t pos = 0, read, totalsize;
210 	tar_header *hdr;
211 	uint32_t sum1, sum2, size, old;
212 	phar_archive_data *myphar, *actual;
213 	int last_was_longlink = 0;
214 	size_t linkname_len;
215 
216 	if (error) {
217 		*error = NULL;
218 	}
219 
220 	php_stream_seek(fp, 0, SEEK_END);
221 	totalsize = php_stream_tell(fp);
222 	php_stream_seek(fp, 0, SEEK_SET);
223 	read = php_stream_read(fp, buf, sizeof(buf));
224 
225 	if (read != sizeof(buf)) {
226 		if (error) {
227 			spprintf(error, 4096, "phar error: \"%s\" is not a tar file or is truncated", fname);
228 		}
229 		php_stream_close(fp);
230 		return FAILURE;
231 	}
232 
233 	hdr = (tar_header*)buf;
234 	old = (memcmp(hdr->magic, "ustar", sizeof("ustar")-1) != 0);
235 
236 	myphar = (phar_archive_data *) pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
237 	myphar->is_persistent = PHAR_G(persist);
238 	/* estimate number of entries, can't be certain with tar files */
239 	zend_hash_init(&myphar->manifest, 2 + (totalsize >> 12),
240 		zend_get_hash_value, destroy_phar_manifest_entry, (bool)myphar->is_persistent);
241 	zend_hash_init(&myphar->mounted_dirs, 5,
242 		zend_get_hash_value, NULL, (bool)myphar->is_persistent);
243 	zend_hash_init(&myphar->virtual_dirs, 4 + (totalsize >> 11),
244 		zend_get_hash_value, NULL, (bool)myphar->is_persistent);
245 	myphar->is_tar = 1;
246 	/* remember whether this entire phar was compressed with gz/bzip2 */
247 	myphar->flags = compression;
248 
249 	entry.is_tar = 1;
250 	entry.is_crc_checked = 1;
251 	entry.phar = myphar;
252 
253 	while (true) {
254 		phar_entry_info *newentry;
255 
256 		pos = php_stream_tell(fp);
257 		hdr = (tar_header*) buf;
258 		sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
259 		if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
260 			break;
261 		}
262 		memset(hdr->checksum, ' ', sizeof(hdr->checksum));
263 		sum2 = phar_tar_checksum(buf, old?sizeof(old_tar_header):sizeof(tar_header));
264 
265 		if (old && sum2 != sum1) {
266 			uint32_t sum3 = phar_tar_checksum(buf, sizeof(tar_header));
267 			if (sum3 == sum1) {
268 				/* apparently a broken tar which is in ustar format w/o setting the ustar marker */
269 				sum2 = sum3;
270 				old = 0;
271 			}
272 		}
273 
274 		size = entry.uncompressed_filesize = entry.compressed_filesize =
275 			phar_tar_number(hdr->size, sizeof(hdr->size));
276 
277 		/* skip global/file headers (pax) */
278 		if (!old && (hdr->typeflag == TAR_GLOBAL_HDR || hdr->typeflag == TAR_FILE_HDR)) {
279 			size = (size+511)&~511;
280 			goto next;
281 		}
282 
283 		if (((!old && hdr->prefix[0] == 0) || old) && zend_strnlen(hdr->name, 100) == sizeof(".phar/signature.bin")-1 && !strncmp(hdr->name, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
284 			zend_off_t curloc;
285 			size_t sig_len;
286 
287 			if (size > 511) {
288 				if (error) {
289 					spprintf(error, 4096, "phar error: tar-based phar \"%s\" has signature that is larger than 511 bytes, cannot process", fname);
290 				}
291 bail:
292 				php_stream_close(fp);
293 				phar_destroy_phar_data(myphar);
294 				return FAILURE;
295 			}
296 			curloc = php_stream_tell(fp);
297 			read = php_stream_read(fp, buf, size);
298 			if (read != size || read <= 8) {
299 				if (error) {
300 					spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be read", fname);
301 				}
302 				goto bail;
303 			}
304 #ifdef WORDS_BIGENDIAN
305 # define PHAR_GET_32(buffer) \
306 	(((((unsigned char*)(buffer))[3]) << 24) \
307 		| ((((unsigned char*)(buffer))[2]) << 16) \
308 		| ((((unsigned char*)(buffer))[1]) <<  8) \
309 		| (((unsigned char*)(buffer))[0]))
310 #else
311 # define PHAR_GET_32(buffer) (uint32_t) *(buffer)
312 #endif
313 			myphar->sig_flags = PHAR_GET_32(buf);
314 			if (FAILURE == phar_verify_signature(fp, php_stream_tell(fp) - size - 512, myphar->sig_flags, buf + 8, size - 8, fname, &myphar->signature, &sig_len, error)) {
315 				if (error) {
316 					char *save = *error;
317 					spprintf(error, 4096, "phar error: tar-based phar \"%s\" signature cannot be verified: %s", fname, save);
318 					efree(save);
319 				}
320 				goto bail;
321 			}
322 			myphar->sig_len = sig_len;
323 			php_stream_seek(fp, curloc + 512, SEEK_SET);
324 			/* signature checked out, let's ensure this is the last file in the phar */
325 			if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
326 				/* this is not good enough - seek succeeds even on truncated tars */
327 				php_stream_seek(fp, 512, SEEK_CUR);
328 				if ((uint32_t)php_stream_tell(fp) > totalsize) {
329 					if (error) {
330 						spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
331 					}
332 					php_stream_close(fp);
333 					phar_destroy_phar_data(myphar);
334 					return FAILURE;
335 				}
336 			}
337 
338 			read = php_stream_read(fp, buf, sizeof(buf));
339 
340 			if (read != sizeof(buf)) {
341 				if (error) {
342 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
343 				}
344 				php_stream_close(fp);
345 				phar_destroy_phar_data(myphar);
346 				return FAILURE;
347 			}
348 
349 			hdr = (tar_header*) buf;
350 			sum1 = phar_tar_number(hdr->checksum, sizeof(hdr->checksum));
351 
352 			if (sum1 == 0 && phar_tar_checksum(buf, sizeof(buf)) == 0) {
353 				break;
354 			}
355 
356 			if (error) {
357 				spprintf(error, 4096, "phar error: \"%s\" has entries after signature, invalid phar", fname);
358 			}
359 
360 			goto bail;
361 		}
362 
363 		if (!last_was_longlink && hdr->typeflag == 'L') {
364 			last_was_longlink = 1;
365 			/* support the ././@LongLink system for storing long filenames */
366 			entry.filename_len = entry.uncompressed_filesize;
367 
368 			/* Check for overflow - bug 61065 */
369 			if (entry.filename_len == UINT_MAX || entry.filename_len == 0) {
370 				if (error) {
371 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (invalid entry size)", fname);
372 				}
373 				php_stream_close(fp);
374 				phar_destroy_phar_data(myphar);
375 				return FAILURE;
376 			}
377 			entry.filename = pemalloc(entry.filename_len+1, myphar->is_persistent);
378 
379 			read = php_stream_read(fp, entry.filename, entry.filename_len);
380 			if (read != entry.filename_len) {
381 				efree(entry.filename);
382 				if (error) {
383 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
384 				}
385 				php_stream_close(fp);
386 				phar_destroy_phar_data(myphar);
387 				return FAILURE;
388 			}
389 			entry.filename[entry.filename_len] = '\0';
390 
391 			/* skip blank stuff */
392 			size = ((size+511)&~511) - size;
393 
394 			/* this is not good enough - seek succeeds even on truncated tars */
395 			php_stream_seek(fp, size, SEEK_CUR);
396 			if ((uint32_t)php_stream_tell(fp) > totalsize) {
397 				efree(entry.filename);
398 				if (error) {
399 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
400 				}
401 				php_stream_close(fp);
402 				phar_destroy_phar_data(myphar);
403 				return FAILURE;
404 			}
405 
406 			read = php_stream_read(fp, buf, sizeof(buf));
407 
408 			if (read != sizeof(buf)) {
409 				efree(entry.filename);
410 				if (error) {
411 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
412 				}
413 				php_stream_close(fp);
414 				phar_destroy_phar_data(myphar);
415 				return FAILURE;
416 			}
417 			continue;
418 		} else if (!last_was_longlink && !old && hdr->prefix[0] != 0) {
419 			char name[256];
420 			int i, j;
421 
422 			for (i = 0; i < 155; i++) {
423 				name[i] = hdr->prefix[i];
424 				if (name[i] == '\0') {
425 					break;
426 				}
427 			}
428 			name[i++] = '/';
429 			for (j = 0; j < 100; j++) {
430 				name[i+j] = hdr->name[j];
431 				if (name[i+j] == '\0') {
432 					break;
433 				}
434 			}
435 
436 			entry.filename_len = i+j;
437 
438 			if (name[entry.filename_len - 1] == '/') {
439 				/* some tar programs store directories with trailing slash */
440 				entry.filename_len--;
441 			}
442 			entry.filename = pestrndup(name, entry.filename_len, myphar->is_persistent);
443 		} else if (!last_was_longlink) {
444 			int i;
445 
446 			/* calculate strlen, which can be no longer than 100 */
447 			for (i = 0; i < 100; i++) {
448 				if (hdr->name[i] == '\0') {
449 					break;
450 				}
451 			}
452 			entry.filename_len = i;
453 			entry.filename = pestrndup(hdr->name, i, myphar->is_persistent);
454 
455 			if (i > 0 && entry.filename[entry.filename_len - 1] == '/') {
456 				/* some tar programs store directories with trailing slash */
457 				entry.filename[entry.filename_len - 1] = '\0';
458 				entry.filename_len--;
459 			}
460 		}
461 		last_was_longlink = 0;
462 
463 		phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len);
464 
465 		if (sum1 != sum2) {
466 			if (error) {
467 				spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename);
468 			}
469 			pefree(entry.filename, myphar->is_persistent);
470 			php_stream_close(fp);
471 			phar_destroy_phar_data(myphar);
472 			return FAILURE;
473 		}
474 
475 		uint32_t entry_mode = phar_tar_number(hdr->mode, sizeof(hdr->mode));
476 		entry.tar_type = ((old & (hdr->typeflag == '\0')) ? TAR_FILE : hdr->typeflag);
477 		entry.offset = entry.offset_abs = pos; /* header_offset unused in tar */
478 		entry.fp_type = PHAR_FP;
479 		entry.flags = entry_mode & PHAR_ENT_PERM_MASK;
480 		entry.timestamp = phar_tar_number(hdr->mtime, sizeof(hdr->mtime));
481 		entry.is_persistent = myphar->is_persistent;
482 
483 		if (old && entry.tar_type == TAR_FILE && S_ISDIR(entry_mode)) {
484 			entry.tar_type = TAR_DIR;
485 		}
486 
487 		if (entry.tar_type == TAR_DIR) {
488 			entry.is_dir = 1;
489 		} else {
490 			entry.is_dir = 0;
491 		}
492 
493 		entry.link = NULL;
494 		/* link field is null-terminated unless it has 100 non-null chars.
495 		 * Thus we cannot use strlen. */
496 		linkname_len = zend_strnlen(hdr->linkname, 100);
497 		if (entry.tar_type == TAR_LINK) {
498 			if (!zend_hash_str_exists(&myphar->manifest, hdr->linkname, linkname_len)) {
499 				if (error) {
500 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file - hard link to non-existent file \"%.*s\"", fname, (int)linkname_len, hdr->linkname);
501 				}
502 				pefree(entry.filename, entry.is_persistent);
503 				php_stream_close(fp);
504 				phar_destroy_phar_data(myphar);
505 				return FAILURE;
506 			}
507 			entry.link = estrndup(hdr->linkname, linkname_len);
508 		} else if (entry.tar_type == TAR_SYMLINK) {
509 			entry.link = estrndup(hdr->linkname, linkname_len);
510 		}
511 		phar_set_inode(&entry);
512 
513 		newentry = zend_hash_str_update_mem(&myphar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
514 		ZEND_ASSERT(newentry != NULL);
515 
516 		if (entry.is_persistent) {
517 			++entry.manifest_pos;
518 		}
519 
520 		if (entry.filename_len >= sizeof(".phar/.metadata")-1 && !memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
521 			if (FAILURE == phar_tar_process_metadata(newentry, fp)) {
522 				if (error) {
523 					spprintf(error, 4096, "phar error: tar-based phar \"%s\" has invalid metadata in magic file \"%s\"", fname, entry.filename);
524 				}
525 				php_stream_close(fp);
526 				phar_destroy_phar_data(myphar);
527 				return FAILURE;
528 			}
529 		}
530 
531 		if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
532 			/* found explicit alias */
533 			if (size > 511) {
534 				if (error) {
535 					spprintf(error, 4096, "phar error: tar-based phar \"%s\" has alias that is larger than 511 bytes, cannot process", fname);
536 				}
537 				php_stream_close(fp);
538 				phar_destroy_phar_data(myphar);
539 				return FAILURE;
540 			}
541 
542 			read = php_stream_read(fp, buf, size);
543 
544 			if (read == size) {
545 				buf[size] = '\0';
546 				if (!phar_validate_alias(buf, size)) {
547 					if (size > 50) {
548 						buf[50] = '.';
549 						buf[51] = '.';
550 						buf[52] = '.';
551 						buf[53] = '\0';
552 					}
553 
554 					if (error) {
555 						spprintf(error, 4096, "phar error: invalid alias \"%s\" in tar-based phar \"%s\"", buf, fname);
556 					}
557 
558 					php_stream_close(fp);
559 					phar_destroy_phar_data(myphar);
560 					return FAILURE;
561 				}
562 
563 				actual_alias = pestrndup(buf, size, myphar->is_persistent);
564 				myphar->alias = actual_alias;
565 				myphar->alias_len = size;
566 				php_stream_seek(fp, pos, SEEK_SET);
567 			} else {
568 				if (error) {
569 					spprintf(error, 4096, "phar error: Unable to read alias from tar-based phar \"%s\"", fname);
570 				}
571 
572 				php_stream_close(fp);
573 				phar_destroy_phar_data(myphar);
574 				return FAILURE;
575 			}
576 		}
577 
578 		size = (size+511)&~511;
579 
580 		if (((hdr->typeflag == '\0') || (hdr->typeflag == TAR_FILE)) && size > 0) {
581 next:
582 			/* this is not good enough - seek succeeds even on truncated tars */
583 			php_stream_seek(fp, size, SEEK_CUR);
584 			if ((uint32_t)php_stream_tell(fp) > totalsize) {
585 				if (error) {
586 					spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
587 				}
588 				php_stream_close(fp);
589 				phar_destroy_phar_data(myphar);
590 				return FAILURE;
591 			}
592 		}
593 
594 		/* Only read next header if we're not yet at the end */
595 		if (php_stream_tell(fp) == totalsize) {
596 			break;
597 		}
598 
599 		read = php_stream_read(fp, buf, sizeof(buf));
600 
601 		if (read != sizeof(buf)) {
602 			if (error) {
603 				spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (truncated)", fname);
604 			}
605 			php_stream_close(fp);
606 			phar_destroy_phar_data(myphar);
607 			return FAILURE;
608 		}
609 	}
610 
611 	if (zend_hash_str_exists(&(myphar->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
612 		myphar->is_data = 0;
613 	} else {
614 		myphar->is_data = 1;
615 	}
616 
617 	/* ensure signature set */
618 	if (!myphar->is_data && PHAR_G(require_hash) && !myphar->signature) {
619 		php_stream_close(fp);
620 		phar_destroy_phar_data(myphar);
621 		if (error) {
622 			spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname);
623 		}
624 		return FAILURE;
625 	}
626 
627 	myphar->fname = pestrndup(fname, fname_len, myphar->is_persistent);
628 #ifdef PHP_WIN32
629 	phar_unixify_path_separators(myphar->fname, fname_len);
630 #endif
631 	myphar->fname_len = fname_len;
632 	myphar->fp = fp;
633 	p = strrchr(myphar->fname, '/');
634 
635 	if (p) {
636 		myphar->ext = memchr(p, '.', (myphar->fname + fname_len) - p);
637 		if (myphar->ext == p) {
638 			myphar->ext = memchr(p + 1, '.', (myphar->fname + fname_len) - p - 1);
639 		}
640 		if (myphar->ext) {
641 			myphar->ext_len = (myphar->fname + fname_len) - myphar->ext;
642 		}
643 	}
644 
645 	phar_request_initialize();
646 
647 	if (NULL == (actual = zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), myphar->fname, fname_len, myphar))) {
648 		if (error) {
649 			spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\" to phar registry", fname);
650 		}
651 		php_stream_close(fp);
652 		phar_destroy_phar_data(myphar);
653 		return FAILURE;
654 	}
655 
656 	myphar = actual;
657 
658 	if (actual_alias) {
659 		phar_archive_data *fd_ptr;
660 
661 		myphar->is_temporary_alias = 0;
662 
663 		if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), actual_alias, myphar->alias_len))) {
664 			if (SUCCESS != phar_free_alias(fd_ptr, actual_alias, myphar->alias_len)) {
665 				if (error) {
666 					spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
667 				}
668 				zend_hash_str_del(&(PHAR_G(phar_fname_map)), myphar->fname, fname_len);
669 				return FAILURE;
670 			}
671 		}
672 
673 		zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, myphar->alias_len, myphar);
674 	} else {
675 		phar_archive_data *fd_ptr;
676 
677 		if (alias_len) {
678 			if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
679 				if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
680 					if (error) {
681 						spprintf(error, 4096, "phar error: Unable to add tar-based phar \"%s\", alias is already in use", fname);
682 					}
683 					zend_hash_str_del(&(PHAR_G(phar_fname_map)), myphar->fname, fname_len);
684 					return FAILURE;
685 				}
686 			}
687 			zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len, myphar);
688 			myphar->alias = pestrndup(alias, alias_len, myphar->is_persistent);
689 			myphar->alias_len = alias_len;
690 		} else {
691 			myphar->alias = pestrndup(myphar->fname, fname_len, myphar->is_persistent);
692 			myphar->alias_len = fname_len;
693 		}
694 
695 		myphar->is_temporary_alias = 1;
696 	}
697 
698 	if (pphar) {
699 		*pphar = myphar;
700 	}
701 
702 	return SUCCESS;
703 }
704 /* }}} */
705 
706 struct _phar_pass_tar_info {
707 	php_stream *old;
708 	php_stream *new;
709 	bool free_fp;
710 	bool free_ufp;
711 	char **error;
712 };
713 
phar_tar_writeheaders_int(phar_entry_info * entry,void * argument)714 static int phar_tar_writeheaders_int(phar_entry_info *entry, void *argument) /* {{{ */
715 {
716 	tar_header header;
717 	size_t pos;
718 	struct _phar_pass_tar_info *fp = (struct _phar_pass_tar_info *)argument;
719 	char padding[512];
720 
721 	if (entry->is_mounted) {
722 		return ZEND_HASH_APPLY_KEEP;
723 	}
724 
725 	if (entry->is_deleted) {
726 		if (entry->fp_refcount <= 0) {
727 			return ZEND_HASH_APPLY_REMOVE;
728 		} else {
729 			/* we can't delete this in-memory until it is closed */
730 			return ZEND_HASH_APPLY_KEEP;
731 		}
732 	}
733 
734 	phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
735 	memset((char *) &header, 0, sizeof(header));
736 
737 	if (entry->filename_len > 100) {
738 		char *boundary;
739 		if (entry->filename_len > 256) {
740 			if (fp->error) {
741 				spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename);
742 			}
743 			return ZEND_HASH_APPLY_STOP;
744 		}
745 		boundary = entry->filename + entry->filename_len - 101;
746 		while (*boundary && *boundary != '/') {
747 			++boundary;
748 		}
749 		if (!*boundary || ((boundary - entry->filename) > 155)) {
750 			if (fp->error) {
751 				spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too long for tar file format", entry->phar->fname, entry->filename);
752 			}
753 			return ZEND_HASH_APPLY_STOP;
754 		}
755 		memcpy(header.prefix, entry->filename, boundary - entry->filename);
756 		memcpy(header.name, boundary + 1, entry->filename_len - (boundary + 1 - entry->filename));
757 	} else {
758 		memcpy(header.name, entry->filename, entry->filename_len);
759 	}
760 
761 	phar_tar_octal(header.mode, entry->flags & PHAR_ENT_PERM_MASK, sizeof(header.mode)-1);
762 
763 	if (FAILURE == phar_tar_octal(header.size, entry->uncompressed_filesize, sizeof(header.size)-1)) {
764 		if (fp->error) {
765 			spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, filename \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
766 		}
767 		return ZEND_HASH_APPLY_STOP;
768 	}
769 
770 	if (FAILURE == phar_tar_octal(header.mtime, entry->timestamp, sizeof(header.mtime)-1)) {
771 		if (fp->error) {
772 			spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, file modification time of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
773 		}
774 		return ZEND_HASH_APPLY_STOP;
775 	}
776 
777 	/* calc checksum */
778 	header.typeflag = entry->tar_type;
779 
780 	if (entry->link) {
781 		if (strlcpy(header.linkname, entry->link, sizeof(header.linkname)) >= sizeof(header.linkname)) {
782 			if (fp->error) {
783 				spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, link \"%s\" is too long for format", entry->phar->fname, entry->link);
784 			}
785 			return ZEND_HASH_APPLY_STOP;
786 		}
787 	}
788 
789 	memcpy(header.magic, "ustar", sizeof("ustar")-1);
790 	memcpy(header.version, "00", sizeof("00")-1);
791 	memcpy(header.checksum, "        ", sizeof("        ")-1);
792 	entry->crc32 = phar_tar_checksum((char *)&header, sizeof(header));
793 
794 	if (FAILURE == phar_tar_octal(header.checksum, entry->crc32, sizeof(header.checksum)-1)) {
795 		if (fp->error) {
796 			spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, checksum of file \"%s\" is too large for tar file format", entry->phar->fname, entry->filename);
797 		}
798 		return ZEND_HASH_APPLY_STOP;
799 	}
800 
801 	/* write header */
802 	entry->header_offset = php_stream_tell(fp->new);
803 
804 	if (sizeof(header) != php_stream_write(fp->new, (char *) &header, sizeof(header))) {
805 		if (fp->error) {
806 			spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, header for  file \"%s\" could not be written", entry->phar->fname, entry->filename);
807 		}
808 		return ZEND_HASH_APPLY_STOP;
809 	}
810 
811 	pos = php_stream_tell(fp->new); /* save start of file within tar */
812 
813 	/* write contents */
814 	if (entry->uncompressed_filesize) {
815 		if (FAILURE == phar_open_entry_fp(entry, fp->error, 0)) {
816 			return ZEND_HASH_APPLY_STOP;
817 		}
818 
819 		if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
820 			if (fp->error) {
821 				spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written, seek failed", entry->phar->fname, entry->filename);
822 			}
823 			return ZEND_HASH_APPLY_STOP;
824 		}
825 
826 		if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), fp->new, entry->uncompressed_filesize, NULL)) {
827 			if (fp->error) {
828 				spprintf(fp->error, 4096, "tar-based phar \"%s\" cannot be created, contents of file \"%s\" could not be written", entry->phar->fname, entry->filename);
829 			}
830 			return ZEND_HASH_APPLY_STOP;
831 		}
832 
833 		memset(padding, 0, 512);
834 		php_stream_write(fp->new, padding, ((entry->uncompressed_filesize +511)&~511) - entry->uncompressed_filesize);
835 	}
836 
837 	if (!entry->is_modified && entry->fp_refcount) {
838 		/* open file pointers refer to this fp, do not free the stream */
839 		switch (entry->fp_type) {
840 			case PHAR_FP:
841 				fp->free_fp = 0;
842 				break;
843 			case PHAR_UFP:
844 				fp->free_ufp = 0;
845 			default:
846 				break;
847 		}
848 	}
849 
850 	entry->is_modified = 0;
851 
852 	if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
853 		if (!entry->fp_refcount) {
854 			php_stream_close(entry->fp);
855 		}
856 		entry->fp = NULL;
857 	}
858 
859 	entry->fp_type = PHAR_FP;
860 
861 	/* note new location within tar */
862 	entry->offset = entry->offset_abs = pos;
863 	return ZEND_HASH_APPLY_KEEP;
864 }
865 /* }}} */
866 
phar_tar_writeheaders(zval * zv,void * argument)867 static int phar_tar_writeheaders(zval *zv, void *argument) /* {{{ */
868 {
869 	return phar_tar_writeheaders_int(Z_PTR_P(zv), argument);
870 }
871 /* }}} */
872 
phar_tar_setmetadata(const phar_metadata_tracker * tracker,phar_entry_info * entry,char ** error)873 static int phar_tar_setmetadata(const phar_metadata_tracker *tracker, phar_entry_info *entry, char **error) /* {{{ */
874 {
875 	/* Copy the metadata from tracker to the new entry being written out to temporary files */
876 	const zend_string *serialized_str;
877 	phar_metadata_tracker_copy(&entry->metadata_tracker, tracker, entry->is_persistent);
878 	phar_metadata_tracker_try_ensure_has_serialized_data(&entry->metadata_tracker, entry->is_persistent);
879 	serialized_str = entry->metadata_tracker.str;
880 
881 	/* If there is no data, this will replace the metadata file (e.g. .phar/.metadata.bin) with an empty file */
882 	entry->uncompressed_filesize = entry->compressed_filesize = serialized_str ? ZSTR_LEN(serialized_str) : 0;
883 
884 	if (entry->fp && entry->fp_type == PHAR_MOD) {
885 		php_stream_close(entry->fp);
886 	}
887 
888 	entry->fp_type = PHAR_MOD;
889 	entry->is_modified = 1;
890 	entry->fp = php_stream_fopen_tmpfile();
891 	entry->offset = entry->offset_abs = 0;
892 	if (entry->fp == NULL) {
893 		spprintf(error, 0, "phar error: unable to create temporary file");
894 		return -1;
895 	}
896 	if (serialized_str && ZSTR_LEN(serialized_str) != php_stream_write(entry->fp, ZSTR_VAL(serialized_str), ZSTR_LEN(serialized_str))) {
897 		spprintf(error, 0, "phar tar error: unable to write metadata to magic metadata file \"%s\"", entry->filename);
898 		zend_hash_str_del(&(entry->phar->manifest), entry->filename, entry->filename_len);
899 		return ZEND_HASH_APPLY_STOP;
900 	}
901 
902 	return ZEND_HASH_APPLY_KEEP;
903 }
904 /* }}} */
905 
phar_tar_setupmetadata(zval * zv,void * argument)906 static int phar_tar_setupmetadata(zval *zv, void *argument) /* {{{ */
907 {
908 	int lookfor_len;
909 	struct _phar_pass_tar_info *i = (struct _phar_pass_tar_info *)argument;
910 	char *lookfor, **error = i->error;
911 	phar_entry_info *entry = (phar_entry_info *)Z_PTR_P(zv), *metadata, newentry = {0};
912 
913 	if (entry->filename_len >= sizeof(".phar/.metadata") && !memcmp(entry->filename, ".phar/.metadata", sizeof(".phar/.metadata")-1)) {
914 		if (entry->filename_len == sizeof(".phar/.metadata.bin")-1 && !memcmp(entry->filename, ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1)) {
915 			return phar_tar_setmetadata(&entry->phar->metadata_tracker, entry, error);
916 		}
917 		/* search for the file this metadata entry references */
918 		if (entry->filename_len >= sizeof(".phar/.metadata/") + sizeof("/.metadata.bin") - 1 && !zend_hash_str_exists(&(entry->phar->manifest), entry->filename + sizeof(".phar/.metadata/") - 1, entry->filename_len - (sizeof("/.metadata.bin") - 1 + sizeof(".phar/.metadata/") - 1))) {
919 			/* this is orphaned metadata, erase it */
920 			return ZEND_HASH_APPLY_REMOVE;
921 		}
922 		/* we can keep this entry, the file that refers to it exists */
923 		return ZEND_HASH_APPLY_KEEP;
924 	}
925 
926 	if (!entry->is_modified) {
927 		return ZEND_HASH_APPLY_KEEP;
928 	}
929 
930 	/* now we are dealing with regular files, so look for metadata */
931 	lookfor_len = spprintf(&lookfor, 0, ".phar/.metadata/%s/.metadata.bin", entry->filename);
932 
933 	if (!phar_metadata_tracker_has_data(&entry->metadata_tracker, entry->is_persistent)) {
934 		zend_hash_str_del(&(entry->phar->manifest), lookfor, lookfor_len);
935 		efree(lookfor);
936 		return ZEND_HASH_APPLY_KEEP;
937 	}
938 
939 	if (NULL != (metadata = zend_hash_str_find_ptr(&(entry->phar->manifest), lookfor, lookfor_len))) {
940 		int ret;
941 		ret = phar_tar_setmetadata(&entry->metadata_tracker, metadata, error);
942 		efree(lookfor);
943 		return ret;
944 	}
945 
946 	newentry.filename = lookfor;
947 	newentry.filename_len = lookfor_len;
948 	newentry.phar = entry->phar;
949 	newentry.tar_type = TAR_FILE;
950 	newentry.is_tar = 1;
951 
952 	if (NULL == (metadata = zend_hash_str_add_mem(&(entry->phar->manifest), lookfor, lookfor_len, (void *)&newentry, sizeof(phar_entry_info)))) {
953 		efree(lookfor);
954 		spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for file \"%s\"", entry->filename);
955 		return ZEND_HASH_APPLY_STOP;
956 	}
957 
958 	return phar_tar_setmetadata(&entry->metadata_tracker, metadata, error);
959 }
960 /* }}} */
961 
phar_tar_flush(phar_archive_data * phar,zend_string * user_stub,bool is_default_stub,char ** error)962 void phar_tar_flush(phar_archive_data *phar, zend_string *user_stub, bool is_default_stub, char **error) /* {{{ */
963 {
964 	static const char newstub[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();";
965 	static const char halt_stub[] = "__HALT_COMPILER();";
966 
967 	phar_entry_info entry = {0};
968 	php_stream *oldfile, *newfile;
969 	bool must_close_old_file = false;
970 	size_t signature_length;
971 	struct _phar_pass_tar_info pass;
972 	char *buf, *signature, sigbuf[8];
973 
974 	entry.flags = PHAR_ENT_PERM_DEF_FILE;
975 	entry.timestamp = time(NULL);
976 	entry.is_modified = 1;
977 	entry.is_crc_checked = 1;
978 	entry.is_tar = 1;
979 	entry.tar_type = '0';
980 	entry.phar = phar;
981 	entry.fp_type = PHAR_MOD;
982 	entry.fp = NULL;
983 	entry.filename = NULL;
984 
985 	if (phar->is_persistent) {
986 		if (error) {
987 			spprintf(error, 0, "internal error: attempt to flush cached tar-based phar \"%s\"", phar->fname);
988 		}
989 		return;
990 	}
991 
992 	if (phar->is_data) {
993 		goto nostub;
994 	}
995 
996 	/* set alias */
997 	if (!phar->is_temporary_alias && phar->alias_len) {
998 		entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
999 		entry.filename_len = sizeof(".phar/alias.txt")-1;
1000 		entry.fp = php_stream_fopen_tmpfile();
1001 		if (entry.fp == NULL) {
1002 			efree(entry.filename);
1003 			spprintf(error, 0, "phar error: unable to create temporary file");
1004 			return;
1005 		}
1006 		if (phar->alias_len != php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1007 			if (error) {
1008 				spprintf(error, 0, "unable to set alias in tar-based phar \"%s\"", phar->fname);
1009 			}
1010 			php_stream_close(entry.fp);
1011 			efree(entry.filename);
1012 			return;
1013 		}
1014 
1015 		entry.uncompressed_filesize = phar->alias_len;
1016 
1017 		zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1018 		/* At this point the entry is saved into the manifest. The manifest destroy
1019 			routine will care about any resources to be freed. */
1020 	} else {
1021 		zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1022 	}
1023 
1024 	/* set stub */
1025 	if (user_stub && !is_default_stub) {
1026 		char *pos = php_stristr(ZSTR_VAL(user_stub), halt_stub, ZSTR_LEN(user_stub), sizeof(halt_stub) - 1);
1027 
1028 		if (pos == NULL) {
1029 			if (error) {
1030 				spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname);
1031 			}
1032 			return;
1033 		}
1034 
1035 		size_t len = pos - ZSTR_VAL(user_stub) + strlen(halt_stub);
1036 		const char end_sequence[] = " ?>\r\n";
1037 		size_t end_sequence_len = strlen(end_sequence);
1038 
1039 		entry.fp = php_stream_fopen_tmpfile();
1040 		if (entry.fp == NULL) {
1041 			spprintf(error, 0, "phar error: unable to create temporary file");
1042 			return;
1043 		}
1044 		entry.uncompressed_filesize = len + end_sequence_len;
1045 
1046 		if (
1047 			len != php_stream_write(entry.fp, ZSTR_VAL(user_stub), len)
1048 			|| end_sequence_len != php_stream_write(entry.fp, end_sequence, end_sequence_len)
1049 		) {
1050 			if (error) {
1051 				spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname);
1052 			}
1053 			php_stream_close(entry.fp);
1054 			return;
1055 		}
1056 
1057 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1058 		entry.filename_len = sizeof(".phar/stub.php")-1;
1059 		zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1060 	} else {
1061 		/* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1062 		entry.fp = php_stream_fopen_tmpfile();
1063 		if (entry.fp == NULL) {
1064 			spprintf(error, 0, "phar error: unable to create temporary file");
1065 			return;
1066 		}
1067 		if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1068 			php_stream_close(entry.fp);
1069 			if (error) {
1070 				spprintf(error, 0, "unable to %s stub in%star-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1071 			}
1072 			return;
1073 		}
1074 
1075 		entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1076 		entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1077 		entry.filename_len = sizeof(".phar/stub.php")-1;
1078 
1079 		if (!is_default_stub) {
1080 			if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1081 				if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1082 					php_stream_close(entry.fp);
1083 					efree(entry.filename);
1084 					if (error) {
1085 						spprintf(error, 0, "unable to create stub in tar-based phar \"%s\"", phar->fname);
1086 					}
1087 					return;
1088 				}
1089 			} else {
1090 				php_stream_close(entry.fp);
1091 				efree(entry.filename);
1092 			}
1093 		} else {
1094 			zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info));
1095 		}
1096 	}
1097 nostub:
1098 	if (phar->fp && !phar->is_brandnew) {
1099 		oldfile = phar->fp;
1100 		must_close_old_file = false;
1101 		php_stream_rewind(oldfile);
1102 	} else {
1103 		oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1104 		must_close_old_file = oldfile != NULL;
1105 	}
1106 
1107 	newfile = php_stream_fopen_tmpfile();
1108 	if (!newfile) {
1109 		if (error) {
1110 			spprintf(error, 0, "unable to create temporary file");
1111 		}
1112 		if (must_close_old_file) {
1113 			php_stream_close(oldfile);
1114 		}
1115 		return;
1116 	}
1117 
1118 	pass.old = oldfile;
1119 	pass.new = newfile;
1120 	pass.error = error;
1121 	pass.free_fp = 1;
1122 	pass.free_ufp = 1;
1123 
1124 	if (phar_metadata_tracker_has_data(&phar->metadata_tracker, phar->is_persistent)) {
1125 		phar_entry_info *mentry;
1126 		if (NULL != (mentry = zend_hash_str_find_ptr(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1))) {
1127 			if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(&phar->metadata_tracker, mentry, error)) {
1128 				if (must_close_old_file) {
1129 					php_stream_close(oldfile);
1130 				}
1131 				return;
1132 			}
1133 		} else {
1134 			phar_entry_info newentry = {0};
1135 
1136 			newentry.filename = estrndup(".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
1137 			newentry.filename_len = sizeof(".phar/.metadata.bin")-1;
1138 			newentry.phar = phar;
1139 			newentry.tar_type = TAR_FILE;
1140 			newentry.is_tar = 1;
1141 
1142 			if (NULL == (mentry = zend_hash_str_add_mem(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1, (void *)&newentry, sizeof(phar_entry_info)))) {
1143 				spprintf(error, 0, "phar tar error: unable to add magic metadata file to manifest for phar archive \"%s\"", phar->fname);
1144 				if (must_close_old_file) {
1145 					php_stream_close(oldfile);
1146 				}
1147 				return;
1148 			}
1149 
1150 			if (ZEND_HASH_APPLY_KEEP != phar_tar_setmetadata(&phar->metadata_tracker, mentry, error)) {
1151 				zend_hash_str_del(&(phar->manifest), ".phar/.metadata.bin", sizeof(".phar/.metadata.bin")-1);
1152 				if (must_close_old_file) {
1153 					php_stream_close(oldfile);
1154 				}
1155 				return;
1156 			}
1157 		}
1158 	}
1159 
1160 	zend_hash_apply_with_argument(&phar->manifest, phar_tar_setupmetadata, (void *) &pass);
1161 
1162 	if (error && *error) {
1163 		if (must_close_old_file) {
1164 			php_stream_close(oldfile);
1165 		}
1166 
1167 		/* on error in the hash iterator above, error is set */
1168 		php_stream_close(newfile);
1169 		return;
1170 	}
1171 
1172 	zend_hash_apply_with_argument(&phar->manifest, phar_tar_writeheaders, (void *) &pass);
1173 
1174 	/* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1175 	if (!phar->is_data || phar->sig_flags) {
1176 		if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, error)) {
1177 			if (error) {
1178 				char *save = *error;
1179 				spprintf(error, 0, "phar error: unable to write signature to tar-based phar: %s", save);
1180 				efree(save);
1181 			}
1182 
1183 			if (must_close_old_file) {
1184 				php_stream_close(oldfile);
1185 			}
1186 
1187 			php_stream_close(newfile);
1188 			return;
1189 		}
1190 
1191 		entry.filename = ".phar/signature.bin";
1192 		entry.filename_len = sizeof(".phar/signature.bin")-1;
1193 		entry.fp = php_stream_fopen_tmpfile();
1194 		if (entry.fp == NULL) {
1195 			spprintf(error, 0, "phar error: unable to create temporary file");
1196 			return;
1197 		}
1198 #ifdef WORDS_BIGENDIAN
1199 # define PHAR_SET_32(destination, source) do { \
1200         uint32_t swapped = (((((unsigned char*)&(source))[3]) << 24) \
1201             | ((((unsigned char*)&(source))[2]) << 16) \
1202             | ((((unsigned char*)&(source))[1]) << 8) \
1203             | (((unsigned char*)&(source))[0])); \
1204         memcpy(destination, &swapped, 4); \
1205     } while (0);
1206 #else
1207 # define PHAR_SET_32(destination, source) memcpy(destination, &source, 4)
1208 #endif
1209 		PHAR_SET_32(sigbuf, phar->sig_flags);
1210 		PHAR_SET_32(sigbuf + 4, signature_length);
1211 
1212 		if (8 != php_stream_write(entry.fp, sigbuf, 8) || signature_length != php_stream_write(entry.fp, signature, signature_length)) {
1213 			efree(signature);
1214 			if (error) {
1215 				spprintf(error, 0, "phar error: unable to write signature to tar-based phar %s", phar->fname);
1216 			}
1217 
1218 			if (must_close_old_file) {
1219 				php_stream_close(oldfile);
1220 			}
1221 			php_stream_close(newfile);
1222 			return;
1223 		}
1224 
1225 		efree(signature);
1226 		entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1227 		/* throw out return value and write the signature */
1228 		entry.filename_len = phar_tar_writeheaders_int(&entry, (void *)&pass);
1229 
1230 		if (error && *error) {
1231 			if (must_close_old_file) {
1232 				php_stream_close(oldfile);
1233 			}
1234 			/* error is set by writeheaders */
1235 			php_stream_close(newfile);
1236 			return;
1237 		}
1238 	} /* signature */
1239 
1240 	/* add final zero blocks */
1241 	buf = (char *) ecalloc(1024, 1);
1242 	php_stream_write(newfile, buf, 1024);
1243 	efree(buf);
1244 
1245 	if (must_close_old_file) {
1246 		php_stream_close(oldfile);
1247 	}
1248 
1249 	/* on error in the hash iterator above, error is set */
1250 	if (error && *error) {
1251 		php_stream_close(newfile);
1252 		return;
1253 	}
1254 
1255 	if (phar->fp && pass.free_fp) {
1256 		php_stream_close(phar->fp);
1257 	}
1258 
1259 	if (phar->ufp) {
1260 		if (pass.free_ufp) {
1261 			php_stream_close(phar->ufp);
1262 		}
1263 		phar->ufp = NULL;
1264 	}
1265 
1266 	phar->is_brandnew = 0;
1267 	php_stream_rewind(newfile);
1268 
1269 	if (phar->donotflush) {
1270 		/* deferred flush */
1271 		phar->fp = newfile;
1272 	} else {
1273 		phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1274 		if (!phar->fp) {
1275 			phar->fp = newfile;
1276 			if (error) {
1277 				spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname);
1278 			}
1279 			return;
1280 		}
1281 
1282 		if (phar->flags & PHAR_FILE_COMPRESSED_GZ) {
1283 			php_stream_filter *filter;
1284 			/* to properly compress, we have to tell zlib to add a zlib header */
1285 			zval filterparams;
1286 
1287 			array_init(&filterparams);
1288 /* this is defined in zlib's zconf.h */
1289 #ifndef MAX_WBITS
1290 #define MAX_WBITS 15
1291 #endif
1292 			add_assoc_long(&filterparams, "window", MAX_WBITS + 16);
1293 			filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp));
1294 			zend_array_destroy(Z_ARR(filterparams));
1295 
1296 			if (!filter) {
1297 				/* copy contents uncompressed rather than lose them */
1298 				php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1299 				php_stream_close(newfile);
1300 				if (error) {
1301 					spprintf(error, 4096, "unable to compress all contents of phar \"%s\" using zlib, PHP versions older than 5.2.6 have a buggy zlib", phar->fname);
1302 				}
1303 				return;
1304 			}
1305 
1306 			php_stream_filter_append(&phar->fp->writefilters, filter);
1307 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1308 			php_stream_filter_flush(filter, 1);
1309 			php_stream_filter_remove(filter, 1);
1310 			php_stream_close(phar->fp);
1311 			/* use the temp stream as our base */
1312 			phar->fp = newfile;
1313 		} else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) {
1314 			php_stream_filter *filter;
1315 
1316 			filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp));
1317 			php_stream_filter_append(&phar->fp->writefilters, filter);
1318 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1319 			php_stream_filter_flush(filter, 1);
1320 			php_stream_filter_remove(filter, 1);
1321 			php_stream_close(phar->fp);
1322 			/* use the temp stream as our base */
1323 			phar->fp = newfile;
1324 		} else {
1325 			php_stream_copy_to_stream_ex(newfile, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1326 			/* we could also reopen the file in "rb" mode but there is no need for that */
1327 			php_stream_close(newfile);
1328 		}
1329 	}
1330 }
1331 /* }}} */
1332