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