1 /*
2 +----------------------------------------------------------------------+
3 | ZIP archive support for Phar |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2007-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt. |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Gregory Beaver <cellog@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "phar_internal.h"
20
21 #define PHAR_GET_16(var) ((uint16_t)((((uint16_t)var[0]) & 0xff) | \
22 (((uint16_t)var[1]) & 0xff) << 8))
23 #define PHAR_GET_32(var) ((uint32_t)((((uint32_t)var[0]) & 0xff) | \
24 (((uint32_t)var[1]) & 0xff) << 8 | \
25 (((uint32_t)var[2]) & 0xff) << 16 | \
26 (((uint32_t)var[3]) & 0xff) << 24))
phar_write_32(char buffer[4],uint32_t value)27 static inline void phar_write_32(char buffer[4], uint32_t value)
28 {
29 buffer[3] = (unsigned char) ((value & 0xff000000) >> 24);
30 buffer[2] = (unsigned char) ((value & 0xff0000) >> 16);
31 buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
32 buffer[0] = (unsigned char) (value & 0xff);
33 }
phar_write_16(char buffer[2],uint32_t value)34 static inline void phar_write_16(char buffer[2], uint32_t value)
35 {
36 buffer[1] = (unsigned char) ((value & 0xff00) >> 8);
37 buffer[0] = (unsigned char) (value & 0xff);
38 }
39 # define PHAR_SET_32(var, value) phar_write_32(var, (uint32_t) (value));
40 # define PHAR_SET_16(var, value) phar_write_16(var, (uint16_t) (value));
41
phar_zip_process_extra(php_stream * fp,phar_entry_info * entry,uint16_t len)42 static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, uint16_t len) /* {{{ */
43 {
44 union {
45 phar_zip_extra_field_header header;
46 phar_zip_unix3 unix3;
47 } h;
48 int read;
49
50 do {
51 if (sizeof(h.header) != php_stream_read(fp, (char *) &h.header, sizeof(h.header))) {
52 return FAILURE;
53 }
54
55 if (h.header.tag[0] != 'n' || h.header.tag[1] != 'u') {
56 /* skip to next header */
57 php_stream_seek(fp, PHAR_GET_16(h.header.size), SEEK_CUR);
58 len -= PHAR_GET_16(h.header.size) + 4;
59 continue;
60 }
61
62 /* unix3 header found */
63 read = php_stream_read(fp, (char *) &(h.unix3.crc32), sizeof(h.unix3) - sizeof(h.header));
64 len -= read + 4;
65
66 if (sizeof(h.unix3) - sizeof(h.header) != read) {
67 return FAILURE;
68 }
69
70 if (PHAR_GET_16(h.unix3.size) > sizeof(h.unix3) - 4) {
71 /* skip symlink filename - we may add this support in later */
72 php_stream_seek(fp, PHAR_GET_16(h.unix3.size) - sizeof(h.unix3.size), SEEK_CUR);
73 }
74
75 /* set permissions */
76 entry->flags &= PHAR_ENT_COMPRESSION_MASK;
77
78 if (entry->is_dir) {
79 entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
80 } else {
81 entry->flags |= PHAR_GET_16(h.unix3.perms) & PHAR_ENT_PERM_MASK;
82 }
83
84 } while (len);
85
86 return SUCCESS;
87 }
88 /* }}} */
89
90 /*
91 extracted from libzip
92 zip_dirent.c -- read directory entry (local or central), clean dirent
93 Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
94
95 This function is part of libzip, a library to manipulate ZIP archives.
96 The authors can be contacted at <nih@giga.or.at>
97
98 Redistribution and use in source and binary forms, with or without
99 modification, are permitted provided that the following conditions
100 are met:
101 1. Redistributions of source code must retain the above copyright
102 notice, this list of conditions and the following disclaimer.
103 2. Redistributions in binary form must reproduce the above copyright
104 notice, this list of conditions and the following disclaimer in
105 the documentation and/or other materials provided with the
106 distribution.
107 3. The names of the authors may not be used to endorse or promote
108 products derived from this software without specific prior
109 written permission.
110
111 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
112 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
113 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
114 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
115 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
116 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
117 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
118 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
119 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
120 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
121 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
122 */
phar_zip_d2u_time(char * cdtime,char * cddate)123 static time_t phar_zip_d2u_time(char *cdtime, char *cddate) /* {{{ */
124 {
125 int dtime = PHAR_GET_16(cdtime), ddate = PHAR_GET_16(cddate);
126 struct tm *tm, tmbuf;
127 time_t now;
128
129 now = time(NULL);
130 tm = php_localtime_r(&now, &tmbuf);
131
132 tm->tm_year = ((ddate>>9)&127) + 1980 - 1900;
133 tm->tm_mon = ((ddate>>5)&15) - 1;
134 tm->tm_mday = ddate&31;
135
136 tm->tm_hour = (dtime>>11)&31;
137 tm->tm_min = (dtime>>5)&63;
138 tm->tm_sec = (dtime<<1)&62;
139
140 return mktime(tm);
141 }
142 /* }}} */
143
phar_zip_u2d_time(time_t time,char * dtime,char * ddate)144 static void phar_zip_u2d_time(time_t time, char *dtime, char *ddate) /* {{{ */
145 {
146 uint16_t ctime, cdate;
147 struct tm *tm, tmbuf;
148
149 tm = php_localtime_r(&time, &tmbuf);
150 cdate = ((tm->tm_year+1900-1980)<<9) + ((tm->tm_mon+1)<<5) + tm->tm_mday;
151 ctime = ((tm->tm_hour)<<11) + ((tm->tm_min)<<5) + ((tm->tm_sec)>>1);
152 PHAR_SET_16(dtime, ctime);
153 PHAR_SET_16(ddate, cdate);
154 }
155 /* }}} */
156
157 /**
158 * Does not check for a previously opened phar in the cache.
159 *
160 * Parse a new one and add it to the cache, returning either SUCCESS or
161 * FAILURE, and setting pphar to the pointer to the manifest entry
162 *
163 * This is used by phar_open_from_fp to process a zip-based phar, but can be called
164 * directly.
165 */
phar_parse_zipfile(php_stream * fp,char * fname,int fname_len,char * alias,int alias_len,phar_archive_data ** pphar,char ** error)166 int phar_parse_zipfile(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error) /* {{{ */
167 {
168 phar_zip_dir_end locator;
169 char buf[sizeof(locator) + 65536];
170 size_t size;
171 uint16_t i;
172 phar_archive_data *mydata = NULL;
173 phar_entry_info entry = {0};
174 char *p = buf, *ext, *actual_alias = NULL;
175 char *metadata = NULL;
176
177 size = php_stream_tell(fp);
178
179 if (size > sizeof(locator) + 65536) {
180 /* seek to max comment length + end of central directory record */
181 size = sizeof(locator) + 65536;
182 if (FAILURE == php_stream_seek(fp, -size, SEEK_END)) {
183 php_stream_close(fp);
184 if (error) {
185 spprintf(error, 4096, "phar error: unable to search for end of central directory in zip-based phar \"%s\"", fname);
186 }
187 return FAILURE;
188 }
189 } else {
190 php_stream_seek(fp, 0, SEEK_SET);
191 }
192
193 if (!php_stream_read(fp, buf, size)) {
194 php_stream_close(fp);
195 if (error) {
196 spprintf(error, 4096, "phar error: unable to read in data to search for end of central directory in zip-based phar \"%s\"", fname);
197 }
198 return FAILURE;
199 }
200
201 while ((p=(char *) memchr(p + 1, 'P', (size_t) (size - (p + 1 - buf)))) != NULL) {
202 if ((p - buf) + sizeof(locator) <= size && !memcmp(p + 1, "K\5\6", 3)) {
203 memcpy((void *)&locator, (void *) p, sizeof(locator));
204 if (PHAR_GET_16(locator.centraldisk) != 0 || PHAR_GET_16(locator.disknumber) != 0) {
205 /* split archives not handled */
206 php_stream_close(fp);
207 if (error) {
208 spprintf(error, 4096, "phar error: split archives spanning multiple zips cannot be processed in zip-based phar \"%s\"", fname);
209 }
210 return FAILURE;
211 }
212
213 if (PHAR_GET_16(locator.counthere) != PHAR_GET_16(locator.count)) {
214 if (error) {
215 spprintf(error, 4096, "phar error: corrupt zip archive, conflicting file count in end of central directory record in zip-based phar \"%s\"", fname);
216 }
217 php_stream_close(fp);
218 return FAILURE;
219 }
220
221 mydata = pecalloc(1, sizeof(phar_archive_data), PHAR_G(persist));
222 mydata->is_persistent = PHAR_G(persist);
223
224 /* read in archive comment, if any */
225 if (PHAR_GET_16(locator.comment_len)) {
226
227 metadata = p + sizeof(locator);
228
229 if (PHAR_GET_16(locator.comment_len) != size - (metadata - buf)) {
230 if (error) {
231 spprintf(error, 4096, "phar error: corrupt zip archive, zip file comment truncated in zip-based phar \"%s\"", fname);
232 }
233 php_stream_close(fp);
234 pefree(mydata, mydata->is_persistent);
235 return FAILURE;
236 }
237
238 mydata->metadata_len = PHAR_GET_16(locator.comment_len);
239
240 if (phar_parse_metadata(&metadata, &mydata->metadata, PHAR_GET_16(locator.comment_len)) == FAILURE) {
241 mydata->metadata_len = 0;
242 /* if not valid serialized data, it is a regular string */
243
244 ZVAL_NEW_STR(&mydata->metadata, zend_string_init(metadata, PHAR_GET_16(locator.comment_len), mydata->is_persistent));
245 }
246 } else {
247 ZVAL_UNDEF(&mydata->metadata);
248 }
249
250 goto foundit;
251 }
252 }
253
254 php_stream_close(fp);
255
256 if (error) {
257 spprintf(error, 4096, "phar error: end of central directory not found in zip-based phar \"%s\"", fname);
258 }
259
260 return FAILURE;
261 foundit:
262 mydata->fname = pestrndup(fname, fname_len, mydata->is_persistent);
263 #ifdef PHP_WIN32
264 phar_unixify_path_separators(mydata->fname, fname_len);
265 #endif
266 mydata->is_zip = 1;
267 mydata->fname_len = fname_len;
268 ext = strrchr(mydata->fname, '/');
269
270 if (ext) {
271 mydata->ext = memchr(ext, '.', (mydata->fname + fname_len) - ext);
272 if (mydata->ext == ext) {
273 mydata->ext = memchr(ext + 1, '.', (mydata->fname + fname_len) - ext - 1);
274 }
275 if (mydata->ext) {
276 mydata->ext_len = (mydata->fname + fname_len) - mydata->ext;
277 }
278 }
279
280 /* clean up on big-endian systems */
281 /* seek to central directory */
282 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
283 /* read in central directory */
284 zend_hash_init(&mydata->manifest, PHAR_GET_16(locator.count),
285 zend_get_hash_value, destroy_phar_manifest_entry, (zend_bool)mydata->is_persistent);
286 zend_hash_init(&mydata->mounted_dirs, 5,
287 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
288 zend_hash_init(&mydata->virtual_dirs, PHAR_GET_16(locator.count) * 2,
289 zend_get_hash_value, NULL, (zend_bool)mydata->is_persistent);
290 entry.phar = mydata;
291 entry.is_zip = 1;
292 entry.fp_type = PHAR_FP;
293 entry.is_persistent = mydata->is_persistent;
294 #define PHAR_ZIP_FAIL_FREE(errmsg, save) \
295 zend_hash_destroy(&mydata->manifest); \
296 mydata->manifest.u.flags = 0; \
297 zend_hash_destroy(&mydata->mounted_dirs); \
298 mydata->mounted_dirs.u.flags = 0; \
299 zend_hash_destroy(&mydata->virtual_dirs); \
300 mydata->virtual_dirs.u.flags = 0; \
301 php_stream_close(fp); \
302 zval_dtor(&mydata->metadata); \
303 if (mydata->signature) { \
304 efree(mydata->signature); \
305 } \
306 if (error) { \
307 spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
308 } \
309 pefree(mydata->fname, mydata->is_persistent); \
310 if (mydata->alias) { \
311 pefree(mydata->alias, mydata->is_persistent); \
312 } \
313 pefree(mydata, mydata->is_persistent); \
314 efree(save); \
315 return FAILURE;
316 #define PHAR_ZIP_FAIL(errmsg) \
317 zend_hash_destroy(&mydata->manifest); \
318 mydata->manifest.u.flags = 0; \
319 zend_hash_destroy(&mydata->mounted_dirs); \
320 mydata->mounted_dirs.u.flags = 0; \
321 zend_hash_destroy(&mydata->virtual_dirs); \
322 mydata->virtual_dirs.u.flags = 0; \
323 php_stream_close(fp); \
324 zval_dtor(&mydata->metadata); \
325 if (mydata->signature) { \
326 efree(mydata->signature); \
327 } \
328 if (error) { \
329 spprintf(error, 4096, "phar error: %s in zip-based phar \"%s\"", errmsg, mydata->fname); \
330 } \
331 pefree(mydata->fname, mydata->is_persistent); \
332 if (mydata->alias) { \
333 pefree(mydata->alias, mydata->is_persistent); \
334 } \
335 pefree(mydata, mydata->is_persistent); \
336 return FAILURE;
337
338 /* add each central directory item to the manifest */
339 for (i = 0; i < PHAR_GET_16(locator.count); ++i) {
340 phar_zip_central_dir_file zipentry;
341 zend_off_t beforeus = php_stream_tell(fp);
342
343 if (sizeof(zipentry) != php_stream_read(fp, (char *) &zipentry, sizeof(zipentry))) {
344 PHAR_ZIP_FAIL("unable to read central directory entry, truncated");
345 }
346
347 /* clean up for bigendian systems */
348 if (memcmp("PK\1\2", zipentry.signature, 4)) {
349 /* corrupted entry */
350 PHAR_ZIP_FAIL("corrupted central directory entry, no magic signature");
351 }
352
353 if (entry.is_persistent) {
354 entry.manifest_pos = i;
355 }
356
357 entry.compressed_filesize = PHAR_GET_32(zipentry.compsize);
358 entry.uncompressed_filesize = PHAR_GET_32(zipentry.uncompsize);
359 entry.crc32 = PHAR_GET_32(zipentry.crc32);
360 /* do not PHAR_GET_16 either on the next line */
361 entry.timestamp = phar_zip_d2u_time(zipentry.timestamp, zipentry.datestamp);
362 entry.flags = PHAR_ENT_PERM_DEF_FILE;
363 entry.header_offset = PHAR_GET_32(zipentry.offset);
364 entry.offset = entry.offset_abs = PHAR_GET_32(zipentry.offset) + sizeof(phar_zip_file_header) + PHAR_GET_16(zipentry.filename_len) +
365 PHAR_GET_16(zipentry.extra_len);
366
367 if (PHAR_GET_16(zipentry.flags) & PHAR_ZIP_FLAG_ENCRYPTED) {
368 PHAR_ZIP_FAIL("Cannot process encrypted zip files");
369 }
370
371 if (!PHAR_GET_16(zipentry.filename_len)) {
372 PHAR_ZIP_FAIL("Cannot process zips created from stdin (zero-length filename)");
373 }
374
375 entry.filename_len = PHAR_GET_16(zipentry.filename_len);
376 entry.filename = (char *) pemalloc(entry.filename_len + 1, entry.is_persistent);
377
378 if (entry.filename_len != php_stream_read(fp, entry.filename, entry.filename_len)) {
379 pefree(entry.filename, entry.is_persistent);
380 PHAR_ZIP_FAIL("unable to read in filename from central directory, truncated");
381 }
382
383 entry.filename[entry.filename_len] = '\0';
384
385 if (entry.filename[entry.filename_len - 1] == '/') {
386 entry.is_dir = 1;
387 if(entry.filename_len > 1) {
388 entry.filename_len--;
389 }
390 entry.flags |= PHAR_ENT_PERM_DEF_DIR;
391 } else {
392 entry.is_dir = 0;
393 }
394
395 if (entry.filename_len == sizeof(".phar/signature.bin")-1 && !strncmp(entry.filename, ".phar/signature.bin", sizeof(".phar/signature.bin")-1)) {
396 size_t read;
397 php_stream *sigfile;
398 char *sig;
399
400 php_stream_tell(fp);
401 pefree(entry.filename, entry.is_persistent);
402 sigfile = php_stream_fopen_tmpfile();
403 if (!sigfile) {
404 PHAR_ZIP_FAIL("couldn't open temporary file");
405 }
406
407 php_stream_seek(fp, 0, SEEK_SET);
408 /* copy file contents + local headers and zip comment, if any, to be hashed for signature */
409 php_stream_copy_to_stream_ex(fp, sigfile, entry.header_offset, NULL);
410 /* seek to central directory */
411 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
412 /* copy central directory header */
413 php_stream_copy_to_stream_ex(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
414 if (metadata) {
415 php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
416 }
417 php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
418 sig = (char *) emalloc(entry.uncompressed_filesize);
419 read = php_stream_read(fp, sig, entry.uncompressed_filesize);
420 if (read != entry.uncompressed_filesize || read <= 8) {
421 php_stream_close(sigfile);
422 efree(sig);
423 PHAR_ZIP_FAIL("signature cannot be read");
424 }
425 mydata->sig_flags = PHAR_GET_32(sig);
426 if (FAILURE == phar_verify_signature(sigfile, php_stream_tell(sigfile), mydata->sig_flags, sig + 8, entry.uncompressed_filesize - 8, fname, &mydata->signature, &mydata->sig_len, error)) {
427 efree(sig);
428 if (error) {
429 char *save;
430 php_stream_close(sigfile);
431 spprintf(&save, 4096, "signature cannot be verified: %s", *error);
432 efree(*error);
433 PHAR_ZIP_FAIL_FREE(save, save);
434 } else {
435 php_stream_close(sigfile);
436 PHAR_ZIP_FAIL("signature cannot be verified");
437 }
438 }
439 php_stream_close(sigfile);
440 efree(sig);
441 /* signature checked out, let's ensure this is the last file in the phar */
442 if (i != PHAR_GET_16(locator.count) - 1) {
443 PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
444 }
445
446 continue;
447 }
448
449 phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len);
450
451 if (PHAR_GET_16(zipentry.extra_len)) {
452 zend_off_t loc = php_stream_tell(fp);
453 if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len))) {
454 pefree(entry.filename, entry.is_persistent);
455 PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
456 }
457 php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
458 }
459
460 switch (PHAR_GET_16(zipentry.compressed)) {
461 case PHAR_ZIP_COMP_NONE :
462 /* compression flag already set */
463 break;
464 case PHAR_ZIP_COMP_DEFLATE :
465 entry.flags |= PHAR_ENT_COMPRESSED_GZ;
466 if (!PHAR_G(has_zlib)) {
467 pefree(entry.filename, entry.is_persistent);
468 PHAR_ZIP_FAIL("zlib extension is required");
469 }
470 break;
471 case PHAR_ZIP_COMP_BZIP2 :
472 entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
473 if (!PHAR_G(has_bz2)) {
474 pefree(entry.filename, entry.is_persistent);
475 PHAR_ZIP_FAIL("bzip2 extension is required");
476 }
477 break;
478 case 1 :
479 pefree(entry.filename, entry.is_persistent);
480 PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
481 case 2 :
482 case 3 :
483 case 4 :
484 case 5 :
485 pefree(entry.filename, entry.is_persistent);
486 PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
487 case 6 :
488 pefree(entry.filename, entry.is_persistent);
489 PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
490 case 7 :
491 pefree(entry.filename, entry.is_persistent);
492 PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
493 case 9 :
494 pefree(entry.filename, entry.is_persistent);
495 PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
496 case 10 :
497 pefree(entry.filename, entry.is_persistent);
498 PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
499 case 14 :
500 pefree(entry.filename, entry.is_persistent);
501 PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
502 case 18 :
503 pefree(entry.filename, entry.is_persistent);
504 PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
505 case 19 :
506 pefree(entry.filename, entry.is_persistent);
507 PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
508 case 97 :
509 pefree(entry.filename, entry.is_persistent);
510 PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
511 case 98 :
512 pefree(entry.filename, entry.is_persistent);
513 PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
514 default :
515 pefree(entry.filename, entry.is_persistent);
516 PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
517 }
518
519 /* get file metadata */
520 if (PHAR_GET_16(zipentry.comment_len)) {
521 if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
522 pefree(entry.filename, entry.is_persistent);
523 PHAR_ZIP_FAIL("unable to read in file comment, truncated");
524 }
525
526 p = buf;
527 entry.metadata_len = PHAR_GET_16(zipentry.comment_len);
528
529 if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len)) == FAILURE) {
530 entry.metadata_len = 0;
531 /* if not valid serialized data, it is a regular string */
532
533 ZVAL_NEW_STR(&entry.metadata, zend_string_init(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent));
534 }
535 } else {
536 ZVAL_UNDEF(&entry.metadata);
537 }
538
539 if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
540 php_stream_filter *filter;
541 zend_off_t saveloc;
542 /* verify local file header */
543 phar_zip_file_header local;
544
545 /* archive alias found */
546 saveloc = php_stream_tell(fp);
547 php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
548
549 if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
550 pefree(entry.filename, entry.is_persistent);
551 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
552 }
553
554 /* verify local header */
555 if (entry.filename_len != PHAR_GET_16(local.filename_len) || entry.crc32 != PHAR_GET_32(local.crc32) || entry.uncompressed_filesize != PHAR_GET_32(local.uncompsize) || entry.compressed_filesize != PHAR_GET_32(local.compsize)) {
556 pefree(entry.filename, entry.is_persistent);
557 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
558 }
559
560 /* construct actual offset to file start - local extra_len can be different from central extra_len */
561 entry.offset = entry.offset_abs =
562 sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
563 php_stream_seek(fp, entry.offset, SEEK_SET);
564 /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
565 fp->writepos = 0;
566 fp->readpos = 0;
567 php_stream_seek(fp, entry.offset, SEEK_SET);
568 fp->writepos = 0;
569 fp->readpos = 0;
570 /* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
571
572 mydata->alias_len = entry.uncompressed_filesize;
573 if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
574 filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp));
575
576 if (!filter) {
577 pefree(entry.filename, entry.is_persistent);
578 PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
579 }
580
581 php_stream_filter_append(&fp->readfilters, filter);
582
583 // TODO: refactor to avoid reallocation ???
584 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
585 {
586 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
587 if (str) {
588 entry.uncompressed_filesize = ZSTR_LEN(str);
589 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
590 zend_string_release(str);
591 } else {
592 actual_alias = NULL;
593 entry.uncompressed_filesize = 0;
594 }
595 }
596
597 if (!entry.uncompressed_filesize || !actual_alias) {
598 pefree(entry.filename, entry.is_persistent);
599 PHAR_ZIP_FAIL("unable to read in alias, truncated");
600 }
601
602 php_stream_filter_flush(filter, 1);
603 php_stream_filter_remove(filter, 1);
604
605 } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
606 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
607
608 if (!filter) {
609 pefree(entry.filename, entry.is_persistent);
610 PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
611 }
612
613 php_stream_filter_append(&fp->readfilters, filter);
614
615 // TODO: refactor to avoid reallocation ???
616 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
617 {
618 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
619 if (str) {
620 entry.uncompressed_filesize = ZSTR_LEN(str);
621 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
622 zend_string_release(str);
623 } else {
624 actual_alias = NULL;
625 entry.uncompressed_filesize = 0;
626 }
627 }
628
629 if (!entry.uncompressed_filesize || !actual_alias) {
630 pefree(entry.filename, entry.is_persistent);
631 PHAR_ZIP_FAIL("unable to read in alias, truncated");
632 }
633
634 php_stream_filter_flush(filter, 1);
635 php_stream_filter_remove(filter, 1);
636 } else {
637 // TODO: refactor to avoid reallocation ???
638 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
639 {
640 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
641 if (str) {
642 entry.uncompressed_filesize = ZSTR_LEN(str);
643 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
644 zend_string_release(str);
645 } else {
646 actual_alias = NULL;
647 entry.uncompressed_filesize = 0;
648 }
649 }
650
651 if (!entry.uncompressed_filesize || !actual_alias) {
652 pefree(entry.filename, entry.is_persistent);
653 PHAR_ZIP_FAIL("unable to read in alias, truncated");
654 }
655 }
656
657 /* return to central directory parsing */
658 php_stream_seek(fp, saveloc, SEEK_SET);
659 }
660
661 phar_set_inode(&entry);
662 zend_hash_str_add_mem(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry, sizeof(phar_entry_info));
663 }
664
665 mydata->fp = fp;
666
667 if (zend_hash_str_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
668 mydata->is_data = 0;
669 } else {
670 mydata->is_data = 1;
671 }
672
673 zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
674
675 if (actual_alias) {
676 phar_archive_data *fd_ptr;
677
678 if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
679 if (error) {
680 spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
681 }
682 efree(actual_alias);
683 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
684 return FAILURE;
685 }
686
687 mydata->is_temporary_alias = 0;
688
689 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len))) {
690 if (SUCCESS != phar_free_alias(fd_ptr, actual_alias, mydata->alias_len)) {
691 if (error) {
692 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
693 }
694 efree(actual_alias);
695 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
696 return FAILURE;
697 }
698 }
699
700 mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
701
702 if (entry.is_persistent) {
703 efree(actual_alias);
704 }
705
706 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), mydata->alias, mydata->alias_len, mydata);
707 } else {
708 phar_archive_data *fd_ptr;
709
710 if (alias_len) {
711 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
712 if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
713 if (error) {
714 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
715 }
716 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
717 return FAILURE;
718 }
719 }
720
721 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len, mydata);
722 mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
723 mydata->alias_len = alias_len;
724 } else {
725 mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
726 mydata->alias_len = fname_len;
727 }
728
729 mydata->is_temporary_alias = 1;
730 }
731
732 if (pphar) {
733 *pphar = mydata;
734 }
735
736 return SUCCESS;
737 }
738 /* }}} */
739
740 /**
741 * Create or open a zip-based phar for writing
742 */
phar_open_or_create_zip(char * fname,int fname_len,char * alias,int alias_len,int is_data,int options,phar_archive_data ** pphar,char ** error)743 int phar_open_or_create_zip(char *fname, int fname_len, char *alias, int alias_len, int is_data, int options, phar_archive_data** pphar, char **error) /* {{{ */
744 {
745 phar_archive_data *phar;
746 int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error);
747
748 if (FAILURE == ret) {
749 return FAILURE;
750 }
751
752 if (pphar) {
753 *pphar = phar;
754 }
755
756 phar->is_data = is_data;
757
758 if (phar->is_zip) {
759 return ret;
760 }
761
762 if (phar->is_brandnew) {
763 phar->internal_file_start = 0;
764 phar->is_zip = 1;
765 phar->is_tar = 0;
766 return SUCCESS;
767 }
768
769 /* we've reached here - the phar exists and is a regular phar */
770 if (error) {
771 spprintf(error, 4096, "phar zip error: phar \"%s\" already exists as a regular phar and must be deleted from disk prior to creating as a zip-based phar", fname);
772 }
773
774 return FAILURE;
775 }
776 /* }}} */
777
778 struct _phar_zip_pass {
779 php_stream *filefp;
780 php_stream *centralfp;
781 php_stream *old;
782 int free_fp;
783 int free_ufp;
784 char **error;
785 };
786 /* perform final modification of zip contents for each file in the manifest before saving */
phar_zip_changed_apply_int(phar_entry_info * entry,void * arg)787 static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ */
788 {
789 phar_zip_file_header local;
790 phar_zip_unix3 perms;
791 phar_zip_central_dir_file central;
792 struct _phar_zip_pass *p;
793 uint32_t newcrc32;
794 zend_off_t offset;
795 int not_really_modified = 0;
796 p = (struct _phar_zip_pass*) arg;
797
798 if (entry->is_mounted) {
799 return ZEND_HASH_APPLY_KEEP;
800 }
801
802 if (entry->is_deleted) {
803 if (entry->fp_refcount <= 0) {
804 return ZEND_HASH_APPLY_REMOVE;
805 } else {
806 /* we can't delete this in-memory until it is closed */
807 return ZEND_HASH_APPLY_KEEP;
808 }
809 }
810
811 phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
812 memset(&local, 0, sizeof(local));
813 memset(¢ral, 0, sizeof(central));
814 memset(&perms, 0, sizeof(perms));
815 strncpy(local.signature, "PK\3\4", 4);
816 strncpy(central.signature, "PK\1\2", 4);
817 PHAR_SET_16(central.extra_len, sizeof(perms));
818 PHAR_SET_16(local.extra_len, sizeof(perms));
819 perms.tag[0] = 'n';
820 perms.tag[1] = 'u';
821 PHAR_SET_16(perms.size, sizeof(perms) - 4);
822 PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
823 {
824 uint32_t crc = (uint32_t) ~0;
825 CRC32(crc, perms.perms[0]);
826 CRC32(crc, perms.perms[1]);
827 PHAR_SET_32(perms.crc32, ~crc);
828 }
829
830 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
831 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
832 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
833 }
834
835 if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
836 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
837 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
838 }
839
840 /* do not use PHAR_GET_16 on either field of the next line */
841 phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
842 memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
843 memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
844 PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
845 PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
846 PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
847
848 /* do extra field for perms later */
849 if (entry->is_modified) {
850 uint32_t loc;
851 php_stream_filter *filter;
852 php_stream *efp;
853
854 if (entry->is_dir) {
855 entry->is_modified = 0;
856 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
857 php_stream_close(entry->fp);
858 entry->fp = NULL;
859 entry->fp_type = PHAR_FP;
860 }
861 goto continue_dir;
862 }
863
864 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
865 spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
866 return ZEND_HASH_APPLY_STOP;
867 }
868
869 /* we can be modified and already be compressed, such as when chmod() is executed */
870 if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
871 not_really_modified = 1;
872 goto is_compressed;
873 }
874
875 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
876 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
877 return ZEND_HASH_APPLY_STOP;
878 }
879
880 efp = phar_get_efp(entry, 0);
881 newcrc32 = ~0;
882
883 for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
884 CRC32(newcrc32, php_stream_getc(efp));
885 }
886
887 entry->crc32 = ~newcrc32;
888 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
889 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
890
891 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
892 /* not compressed */
893 entry->compressed_filesize = entry->uncompressed_filesize;
894 PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
895 PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
896 goto not_compressed;
897 }
898
899 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
900
901 if (!filter) {
902 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
903 spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
904 } else {
905 spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
906 }
907 return ZEND_HASH_APPLY_STOP;
908 }
909
910 /* create new file that holds the compressed version */
911 /* work around inability to specify freedom in write and strictness
912 in read count */
913 entry->cfp = php_stream_fopen_tmpfile();
914
915 if (!entry->cfp) {
916 spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
917 return ZEND_HASH_APPLY_STOP;
918 }
919
920 php_stream_flush(efp);
921
922 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
923 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
924 return ZEND_HASH_APPLY_STOP;
925 }
926
927 php_stream_filter_append((&entry->cfp->writefilters), filter);
928
929 if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
930 spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
931 return ZEND_HASH_APPLY_STOP;
932 }
933
934 php_stream_filter_flush(filter, 1);
935 php_stream_flush(entry->cfp);
936 php_stream_filter_remove(filter, 1);
937 php_stream_seek(entry->cfp, 0, SEEK_END);
938 entry->compressed_filesize = (uint32_t) php_stream_tell(entry->cfp);
939 PHAR_SET_32(central.compsize, entry->compressed_filesize);
940 PHAR_SET_32(local.compsize, entry->compressed_filesize);
941 /* generate crc on compressed file */
942 php_stream_rewind(entry->cfp);
943 entry->old_flags = entry->flags;
944 entry->is_modified = 1;
945 } else {
946 is_compressed:
947 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
948 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
949 PHAR_SET_32(central.compsize, entry->compressed_filesize);
950 PHAR_SET_32(local.compsize, entry->compressed_filesize);
951 if (p->old) {
952 if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
953 spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
954 return ZEND_HASH_APPLY_STOP;
955 }
956 }
957 }
958 not_compressed:
959 PHAR_SET_32(central.crc32, entry->crc32);
960 PHAR_SET_32(local.crc32, entry->crc32);
961 continue_dir:
962 /* set file metadata */
963 if (Z_TYPE(entry->metadata) != IS_UNDEF) {
964 php_serialize_data_t metadata_hash;
965
966 if (entry->metadata_str.s) {
967 smart_str_free(&entry->metadata_str);
968 }
969 entry->metadata_str.s = NULL;
970 PHP_VAR_SERIALIZE_INIT(metadata_hash);
971 php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
972 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
973 PHAR_SET_16(central.comment_len, ZSTR_LEN(entry->metadata_str.s));
974 }
975
976 entry->header_offset = php_stream_tell(p->filefp);
977 offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
978
979 if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
980 spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
981 return ZEND_HASH_APPLY_STOP;
982 }
983
984 if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) {
985 spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
986 return ZEND_HASH_APPLY_STOP;
987 }
988
989 if (entry->is_dir) {
990 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
991 spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
992 return ZEND_HASH_APPLY_STOP;
993 }
994
995 if (1 != php_stream_write(p->filefp, "/", 1)) {
996 spprintf(p->error, 0, "unable to write filename to local directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
997 return ZEND_HASH_APPLY_STOP;
998 }
999
1000 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1001 spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1002 return ZEND_HASH_APPLY_STOP;
1003 }
1004
1005 if (1 != php_stream_write(p->centralfp, "/", 1)) {
1006 spprintf(p->error, 0, "unable to write filename to central directory entry for directory \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1007 return ZEND_HASH_APPLY_STOP;
1008 }
1009 } else {
1010 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1011 spprintf(p->error, 0, "unable to write filename to local directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1012 return ZEND_HASH_APPLY_STOP;
1013 }
1014
1015 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1016 spprintf(p->error, 0, "unable to write filename to central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1017 return ZEND_HASH_APPLY_STOP;
1018 }
1019 }
1020
1021 if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1022 spprintf(p->error, 0, "unable to write local extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1023 return ZEND_HASH_APPLY_STOP;
1024 }
1025
1026 if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1027 spprintf(p->error, 0, "unable to write central extra permissions file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1028 return ZEND_HASH_APPLY_STOP;
1029 }
1030
1031 if (!not_really_modified && entry->is_modified) {
1032 if (entry->cfp) {
1033 if (SUCCESS != php_stream_copy_to_stream_ex(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1034 spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1035 return ZEND_HASH_APPLY_STOP;
1036 }
1037
1038 php_stream_close(entry->cfp);
1039 entry->cfp = NULL;
1040 } else {
1041 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
1042 return ZEND_HASH_APPLY_STOP;
1043 }
1044
1045 phar_seek_efp(entry, 0, SEEK_SET, 0, 0);
1046
1047 if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), p->filefp, entry->uncompressed_filesize, NULL)) {
1048 spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1049 return ZEND_HASH_APPLY_STOP;
1050 }
1051 }
1052
1053 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1054 php_stream_close(entry->fp);
1055 }
1056
1057 entry->is_modified = 0;
1058 } else {
1059 entry->is_modified = 0;
1060 if (entry->fp_refcount) {
1061 /* open file pointers refer to this fp, do not free the stream */
1062 switch (entry->fp_type) {
1063 case PHAR_FP:
1064 p->free_fp = 0;
1065 break;
1066 case PHAR_UFP:
1067 p->free_ufp = 0;
1068 default:
1069 break;
1070 }
1071 }
1072
1073 if (!entry->is_dir && entry->compressed_filesize && SUCCESS != php_stream_copy_to_stream_ex(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1074 spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1075 return ZEND_HASH_APPLY_STOP;
1076 }
1077 }
1078
1079 entry->fp = NULL;
1080 entry->offset = entry->offset_abs = offset;
1081 entry->fp_type = PHAR_FP;
1082
1083 if (entry->metadata_str.s) {
1084 if (ZSTR_LEN(entry->metadata_str.s) != php_stream_write(p->centralfp, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s))) {
1085 spprintf(p->error, 0, "unable to write metadata as file comment for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1086 smart_str_free(&entry->metadata_str);
1087 return ZEND_HASH_APPLY_STOP;
1088 }
1089
1090 smart_str_free(&entry->metadata_str);
1091 }
1092
1093 return ZEND_HASH_APPLY_KEEP;
1094 }
1095 /* }}} */
1096
phar_zip_changed_apply(zval * zv,void * arg)1097 static int phar_zip_changed_apply(zval *zv, void *arg) /* {{{ */
1098 {
1099 return phar_zip_changed_apply_int(Z_PTR_P(zv), arg);
1100 }
1101 /* }}} */
1102
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass,smart_str * metadata)1103 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
1104 smart_str *metadata) /* {{{ */
1105 {
1106 /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1107 if (!phar->is_data || phar->sig_flags) {
1108 int signature_length;
1109 char *signature, sigbuf[8];
1110 phar_entry_info entry = {0};
1111 php_stream *newfile;
1112 zend_off_t tell;
1113
1114 newfile = php_stream_fopen_tmpfile();
1115 if (newfile == NULL) {
1116 spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1117 return FAILURE;
1118 }
1119 tell = php_stream_tell(pass->filefp);
1120 /* copy the local files, central directory, and the zip comment to generate the hash */
1121 php_stream_seek(pass->filefp, 0, SEEK_SET);
1122 php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
1123 tell = php_stream_tell(pass->centralfp);
1124 php_stream_seek(pass->centralfp, 0, SEEK_SET);
1125 php_stream_copy_to_stream_ex(pass->centralfp, newfile, tell, NULL);
1126 if (metadata->s) {
1127 php_stream_write(newfile, ZSTR_VAL(metadata->s), ZSTR_LEN(metadata->s));
1128 }
1129
1130 if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error)) {
1131 if (pass->error) {
1132 char *save = *(pass->error);
1133 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1134 efree(save);
1135 }
1136
1137 php_stream_close(newfile);
1138 return FAILURE;
1139 }
1140
1141 entry.filename = ".phar/signature.bin";
1142 entry.filename_len = sizeof(".phar/signature.bin")-1;
1143 entry.fp = php_stream_fopen_tmpfile();
1144 entry.fp_type = PHAR_MOD;
1145 entry.is_modified = 1;
1146 if (entry.fp == NULL) {
1147 spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1148 return FAILURE;
1149 }
1150
1151 PHAR_SET_32(sigbuf, phar->sig_flags);
1152 PHAR_SET_32(sigbuf + 4, signature_length);
1153
1154 if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
1155 efree(signature);
1156 if (pass->error) {
1157 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1158 }
1159
1160 php_stream_close(newfile);
1161 return FAILURE;
1162 }
1163
1164 efree(signature);
1165 entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1166 entry.phar = phar;
1167 /* throw out return value and write the signature */
1168 phar_zip_changed_apply_int(&entry, (void *)pass);
1169 php_stream_close(newfile);
1170
1171 if (pass->error && *(pass->error)) {
1172 /* error is set by writeheaders */
1173 return FAILURE;
1174 }
1175 } /* signature */
1176 return SUCCESS;
1177 }
1178 /* }}} */
1179
phar_zip_flush(phar_archive_data * phar,char * user_stub,zend_long len,int defaultstub,char ** error)1180 int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error) /* {{{ */
1181 {
1182 char *pos;
1183 smart_str main_metadata_str = {0};
1184 static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1185 char halt_stub[] = "__HALT_COMPILER();";
1186 char *tmp;
1187
1188 php_stream *stubfile, *oldfile;
1189 php_serialize_data_t metadata_hash;
1190 int free_user_stub, closeoldfile = 0;
1191 phar_entry_info entry = {0};
1192 char *temperr = NULL;
1193 struct _phar_zip_pass pass;
1194 phar_zip_dir_end eocd;
1195 uint32_t cdir_size, cdir_offset;
1196
1197 pass.error = &temperr;
1198 entry.flags = PHAR_ENT_PERM_DEF_FILE;
1199 entry.timestamp = time(NULL);
1200 entry.is_modified = 1;
1201 entry.is_zip = 1;
1202 entry.phar = phar;
1203 entry.fp_type = PHAR_MOD;
1204
1205 if (phar->is_persistent) {
1206 if (error) {
1207 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1208 }
1209 return EOF;
1210 }
1211
1212 if (phar->is_data) {
1213 goto nostub;
1214 }
1215
1216 /* set alias */
1217 if (!phar->is_temporary_alias && phar->alias_len) {
1218 entry.fp = php_stream_fopen_tmpfile();
1219 if (entry.fp == NULL) {
1220 spprintf(error, 0, "phar error: unable to create temporary file");
1221 return EOF;
1222 }
1223 if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1224 if (error) {
1225 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1226 }
1227 return EOF;
1228 }
1229
1230 entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1231 entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1232 entry.filename_len = sizeof(".phar/alias.txt")-1;
1233
1234 if (NULL == zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1235 if (error) {
1236 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1237 }
1238 return EOF;
1239 }
1240 } else {
1241 zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1242 }
1243
1244 /* register alias */
1245 if (phar->alias_len) {
1246 if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error)) {
1247 return EOF;
1248 }
1249 }
1250
1251 /* set stub */
1252 if (user_stub && !defaultstub) {
1253 if (len < 0) {
1254 /* resource passed in */
1255 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
1256 if (error) {
1257 spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1258 }
1259 return EOF;
1260 }
1261
1262 if (len == -1) {
1263 len = PHP_STREAM_COPY_ALL;
1264 } else {
1265 len = -len;
1266 }
1267
1268 user_stub = 0;
1269
1270 // TODO: refactor to avoid reallocation ???
1271 //??? len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)
1272 {
1273 zend_string *str = php_stream_copy_to_mem(stubfile, len, 0);
1274 if (str) {
1275 len = ZSTR_LEN(str);
1276 user_stub = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
1277 zend_string_release(str);
1278 } else {
1279 user_stub = NULL;
1280 len = 0;
1281 }
1282 }
1283
1284 if (!len || !user_stub) {
1285 if (error) {
1286 spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1287 }
1288 return EOF;
1289 }
1290 free_user_stub = 1;
1291 } else {
1292 free_user_stub = 0;
1293 }
1294
1295 tmp = estrndup(user_stub, len);
1296 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1297 efree(tmp);
1298 if (error) {
1299 spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1300 }
1301 if (free_user_stub) {
1302 efree(user_stub);
1303 }
1304 return EOF;
1305 }
1306 pos = user_stub + (pos - tmp);
1307 efree(tmp);
1308
1309 len = pos - user_stub + 18;
1310 entry.fp = php_stream_fopen_tmpfile();
1311 if (entry.fp == NULL) {
1312 spprintf(error, 0, "phar error: unable to create temporary file");
1313 return EOF;
1314 }
1315 entry.uncompressed_filesize = len + 5;
1316
1317 if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1318 || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1319 if (error) {
1320 spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1321 }
1322 if (free_user_stub) {
1323 efree(user_stub);
1324 }
1325 php_stream_close(entry.fp);
1326 return EOF;
1327 }
1328
1329 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1330 entry.filename_len = sizeof(".phar/stub.php")-1;
1331
1332 if (NULL == zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1333 if (free_user_stub) {
1334 efree(user_stub);
1335 }
1336 if (error) {
1337 spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
1338 }
1339 return EOF;
1340 }
1341
1342 if (free_user_stub) {
1343 efree(user_stub);
1344 }
1345 } else {
1346 /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1347 entry.fp = php_stream_fopen_tmpfile();
1348 if (entry.fp == NULL) {
1349 spprintf(error, 0, "phar error: unable to create temporary file");
1350 return EOF;
1351 }
1352 if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1353 php_stream_close(entry.fp);
1354 if (error) {
1355 spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1356 }
1357 return EOF;
1358 }
1359
1360 entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1361 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1362 entry.filename_len = sizeof(".phar/stub.php")-1;
1363
1364 if (!defaultstub) {
1365 if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1366 if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1367 php_stream_close(entry.fp);
1368 efree(entry.filename);
1369 if (error) {
1370 spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1371 }
1372 return EOF;
1373 }
1374 } else {
1375 php_stream_close(entry.fp);
1376 efree(entry.filename);
1377 }
1378 } else {
1379 if (NULL == zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1380 php_stream_close(entry.fp);
1381 efree(entry.filename);
1382 if (error) {
1383 spprintf(error, 0, "unable to overwrite stub in zip-based phar \"%s\"", phar->fname);
1384 }
1385 return EOF;
1386 }
1387 }
1388 }
1389 nostub:
1390 if (phar->fp && !phar->is_brandnew) {
1391 oldfile = phar->fp;
1392 closeoldfile = 0;
1393 php_stream_rewind(oldfile);
1394 } else {
1395 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1396 closeoldfile = oldfile != NULL;
1397 }
1398
1399 /* save modified files to the zip */
1400 pass.old = oldfile;
1401 pass.filefp = php_stream_fopen_tmpfile();
1402
1403 if (!pass.filefp) {
1404 fperror:
1405 if (closeoldfile) {
1406 php_stream_close(oldfile);
1407 }
1408 if (error) {
1409 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1410 }
1411 return EOF;
1412 }
1413
1414 pass.centralfp = php_stream_fopen_tmpfile();
1415
1416 if (!pass.centralfp) {
1417 goto fperror;
1418 }
1419
1420 pass.free_fp = pass.free_ufp = 1;
1421 memset(&eocd, 0, sizeof(eocd));
1422
1423 strncpy(eocd.signature, "PK\5\6", 4);
1424 if (!phar->is_data && !phar->sig_flags) {
1425 phar->sig_flags = PHAR_SIG_SHA1;
1426 }
1427 if (phar->sig_flags) {
1428 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1429 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1430 } else {
1431 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1432 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1433 }
1434 zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass);
1435
1436 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1437 /* set phar metadata */
1438 PHP_VAR_SERIALIZE_INIT(metadata_hash);
1439 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
1440 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1441 }
1442 if (temperr) {
1443 if (error) {
1444 spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1445 }
1446 efree(temperr);
1447 temperror:
1448 php_stream_close(pass.centralfp);
1449 nocentralerror:
1450 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1451 smart_str_free(&main_metadata_str);
1452 }
1453 php_stream_close(pass.filefp);
1454 if (closeoldfile) {
1455 php_stream_close(oldfile);
1456 }
1457 return EOF;
1458 }
1459
1460 if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str)) {
1461 goto temperror;
1462 }
1463
1464 /* save zip */
1465 cdir_size = php_stream_tell(pass.centralfp);
1466 cdir_offset = php_stream_tell(pass.filefp);
1467 PHAR_SET_32(eocd.cdir_size, cdir_size);
1468 PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1469 php_stream_seek(pass.centralfp, 0, SEEK_SET);
1470
1471 {
1472 size_t clen;
1473 int ret = php_stream_copy_to_stream_ex(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1474 if (SUCCESS != ret || clen != cdir_size) {
1475 if (error) {
1476 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1477 }
1478 goto temperror;
1479 }
1480 }
1481
1482 php_stream_close(pass.centralfp);
1483
1484 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1485 /* set phar metadata */
1486 PHAR_SET_16(eocd.comment_len, ZSTR_LEN(main_metadata_str.s));
1487
1488 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1489 if (error) {
1490 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1491 }
1492 goto nocentralerror;
1493 }
1494
1495 if (ZSTR_LEN(main_metadata_str.s) != php_stream_write(pass.filefp, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s))) {
1496 if (error) {
1497 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1498 }
1499 goto nocentralerror;
1500 }
1501
1502 smart_str_free(&main_metadata_str);
1503
1504 } else {
1505 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1506 if (error) {
1507 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1508 }
1509 goto nocentralerror;
1510 }
1511 }
1512
1513 if (phar->fp && pass.free_fp) {
1514 php_stream_close(phar->fp);
1515 }
1516
1517 if (phar->ufp) {
1518 if (pass.free_ufp) {
1519 php_stream_close(phar->ufp);
1520 }
1521 phar->ufp = NULL;
1522 }
1523
1524 /* re-open */
1525 phar->is_brandnew = 0;
1526
1527 if (phar->donotflush) {
1528 /* deferred flush */
1529 phar->fp = pass.filefp;
1530 } else {
1531 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1532 if (!phar->fp) {
1533 if (closeoldfile) {
1534 php_stream_close(oldfile);
1535 }
1536 phar->fp = pass.filefp;
1537 if (error) {
1538 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1539 }
1540 return EOF;
1541 }
1542 php_stream_rewind(pass.filefp);
1543 php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1544 /* we could also reopen the file in "rb" mode but there is no need for that */
1545 php_stream_close(pass.filefp);
1546 }
1547
1548 if (closeoldfile) {
1549 php_stream_close(oldfile);
1550 }
1551 return EOF;
1552 }
1553 /* }}} */
1554
1555 /*
1556 * Local variables:
1557 * tab-width: 4
1558 * c-basic-offset: 4
1559 * End:
1560 * vim600: noet sw=4 ts=4 fdm=marker
1561 * vim<600: noet sw=4 ts=4
1562 */
1563