1 /*
2 +----------------------------------------------------------------------+
3 | ZIP archive support for Phar |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2007-2017 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) ((php_uint16)((((php_uint16)var[0]) & 0xff) | \
22 (((php_uint16)var[1]) & 0xff) << 8))
23 #define PHAR_GET_32(var) ((php_uint32)((((php_uint32)var[0]) & 0xff) | \
24 (((php_uint32)var[1]) & 0xff) << 8 | \
25 (((php_uint32)var[2]) & 0xff) << 16 | \
26 (((php_uint32)var[3]) & 0xff) << 24))
phar_write_32(char buffer[4],php_uint32 value)27 static inline void phar_write_32(char buffer[4], php_uint32 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],php_uint32 value)34 static inline void phar_write_16(char buffer[2], php_uint32 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, (php_uint32) (value));
40 # define PHAR_SET_16(var, value) phar_write_16(var, (php_uint16) (value));
41
phar_zip_process_extra(php_stream * fp,phar_entry_info * entry,php_uint16 len)42 static int phar_zip_process_extra(php_stream *fp, phar_entry_info *entry, php_uint16 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 php_uint16 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 zend_long size;
171 php_uint16 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 zend_off_t now;
399 char *sig;
400
401 now = php_stream_tell(fp);
402 pefree(entry.filename, entry.is_persistent);
403 sigfile = php_stream_fopen_tmpfile();
404 if (!sigfile) {
405 PHAR_ZIP_FAIL("couldn't open temporary file");
406 }
407
408 php_stream_seek(fp, 0, SEEK_SET);
409 /* copy file contents + local headers and zip comment, if any, to be hashed for signature */
410 php_stream_copy_to_stream_ex(fp, sigfile, entry.header_offset, NULL);
411 /* seek to central directory */
412 php_stream_seek(fp, PHAR_GET_32(locator.cdir_offset), SEEK_SET);
413 /* copy central directory header */
414 php_stream_copy_to_stream_ex(fp, sigfile, beforeus - PHAR_GET_32(locator.cdir_offset), NULL);
415 if (metadata) {
416 php_stream_write(sigfile, metadata, PHAR_GET_16(locator.comment_len));
417 }
418 php_stream_seek(fp, sizeof(phar_zip_file_header) + entry.header_offset + entry.filename_len + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
419 sig = (char *) emalloc(entry.uncompressed_filesize);
420 read = php_stream_read(fp, sig, entry.uncompressed_filesize);
421 if (read != entry.uncompressed_filesize || read <= 8) {
422 php_stream_close(sigfile);
423 efree(sig);
424 PHAR_ZIP_FAIL("signature cannot be read");
425 }
426 mydata->sig_flags = PHAR_GET_32(sig);
427 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)) {
428 efree(sig);
429 if (error) {
430 char *save;
431 php_stream_close(sigfile);
432 spprintf(&save, 4096, "signature cannot be verified: %s", *error);
433 efree(*error);
434 PHAR_ZIP_FAIL_FREE(save, save);
435 } else {
436 php_stream_close(sigfile);
437 PHAR_ZIP_FAIL("signature cannot be verified");
438 }
439 }
440 php_stream_close(sigfile);
441 efree(sig);
442 /* signature checked out, let's ensure this is the last file in the phar */
443 if (i != PHAR_GET_16(locator.count) - 1) {
444 PHAR_ZIP_FAIL("entries exist after signature, invalid phar");
445 }
446
447 continue;
448 }
449
450 phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len);
451
452 if (PHAR_GET_16(zipentry.extra_len)) {
453 zend_off_t loc = php_stream_tell(fp);
454 if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len))) {
455 pefree(entry.filename, entry.is_persistent);
456 PHAR_ZIP_FAIL("Unable to process extra field header for file in central directory");
457 }
458 php_stream_seek(fp, loc + PHAR_GET_16(zipentry.extra_len), SEEK_SET);
459 }
460
461 switch (PHAR_GET_16(zipentry.compressed)) {
462 case PHAR_ZIP_COMP_NONE :
463 /* compression flag already set */
464 break;
465 case PHAR_ZIP_COMP_DEFLATE :
466 entry.flags |= PHAR_ENT_COMPRESSED_GZ;
467 if (!PHAR_G(has_zlib)) {
468 pefree(entry.filename, entry.is_persistent);
469 PHAR_ZIP_FAIL("zlib extension is required");
470 }
471 break;
472 case PHAR_ZIP_COMP_BZIP2 :
473 entry.flags |= PHAR_ENT_COMPRESSED_BZ2;
474 if (!PHAR_G(has_bz2)) {
475 pefree(entry.filename, entry.is_persistent);
476 PHAR_ZIP_FAIL("bzip2 extension is required");
477 }
478 break;
479 case 1 :
480 pefree(entry.filename, entry.is_persistent);
481 PHAR_ZIP_FAIL("unsupported compression method (Shrunk) used in this zip");
482 case 2 :
483 case 3 :
484 case 4 :
485 case 5 :
486 pefree(entry.filename, entry.is_persistent);
487 PHAR_ZIP_FAIL("unsupported compression method (Reduce) used in this zip");
488 case 6 :
489 pefree(entry.filename, entry.is_persistent);
490 PHAR_ZIP_FAIL("unsupported compression method (Implode) used in this zip");
491 case 7 :
492 pefree(entry.filename, entry.is_persistent);
493 PHAR_ZIP_FAIL("unsupported compression method (Tokenize) used in this zip");
494 case 9 :
495 pefree(entry.filename, entry.is_persistent);
496 PHAR_ZIP_FAIL("unsupported compression method (Deflate64) used in this zip");
497 case 10 :
498 pefree(entry.filename, entry.is_persistent);
499 PHAR_ZIP_FAIL("unsupported compression method (PKWare Implode/old IBM TERSE) used in this zip");
500 case 14 :
501 pefree(entry.filename, entry.is_persistent);
502 PHAR_ZIP_FAIL("unsupported compression method (LZMA) used in this zip");
503 case 18 :
504 pefree(entry.filename, entry.is_persistent);
505 PHAR_ZIP_FAIL("unsupported compression method (IBM TERSE) used in this zip");
506 case 19 :
507 pefree(entry.filename, entry.is_persistent);
508 PHAR_ZIP_FAIL("unsupported compression method (IBM LZ77) used in this zip");
509 case 97 :
510 pefree(entry.filename, entry.is_persistent);
511 PHAR_ZIP_FAIL("unsupported compression method (WavPack) used in this zip");
512 case 98 :
513 pefree(entry.filename, entry.is_persistent);
514 PHAR_ZIP_FAIL("unsupported compression method (PPMd) used in this zip");
515 default :
516 pefree(entry.filename, entry.is_persistent);
517 PHAR_ZIP_FAIL("unsupported compression method (unknown) used in this zip");
518 }
519
520 /* get file metadata */
521 if (PHAR_GET_16(zipentry.comment_len)) {
522 if (PHAR_GET_16(zipentry.comment_len) != php_stream_read(fp, buf, PHAR_GET_16(zipentry.comment_len))) {
523 pefree(entry.filename, entry.is_persistent);
524 PHAR_ZIP_FAIL("unable to read in file comment, truncated");
525 }
526
527 p = buf;
528 entry.metadata_len = PHAR_GET_16(zipentry.comment_len);
529
530 if (phar_parse_metadata(&p, &(entry.metadata), PHAR_GET_16(zipentry.comment_len)) == FAILURE) {
531 entry.metadata_len = 0;
532 /* if not valid serialized data, it is a regular string */
533
534 ZVAL_NEW_STR(&entry.metadata, zend_string_init(buf, PHAR_GET_16(zipentry.comment_len), entry.is_persistent));
535 }
536 } else {
537 ZVAL_UNDEF(&entry.metadata);
538 }
539
540 if (!actual_alias && entry.filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry.filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
541 php_stream_filter *filter;
542 zend_off_t saveloc;
543 /* verify local file header */
544 phar_zip_file_header local;
545
546 /* archive alias found */
547 saveloc = php_stream_tell(fp);
548 php_stream_seek(fp, PHAR_GET_32(zipentry.offset), SEEK_SET);
549
550 if (sizeof(local) != php_stream_read(fp, (char *) &local, sizeof(local))) {
551 pefree(entry.filename, entry.is_persistent);
552 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (cannot read local file header for alias)");
553 }
554
555 /* verify local header */
556 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)) {
557 pefree(entry.filename, entry.is_persistent);
558 PHAR_ZIP_FAIL("phar error: internal corruption of zip-based phar (local header of alias does not match central directory)");
559 }
560
561 /* construct actual offset to file start - local extra_len can be different from central extra_len */
562 entry.offset = entry.offset_abs =
563 sizeof(local) + entry.header_offset + PHAR_GET_16(local.filename_len) + PHAR_GET_16(local.extra_len);
564 php_stream_seek(fp, entry.offset, SEEK_SET);
565 /* these next lines should be for php < 5.2.6 after 5.3 filters are fixed */
566 fp->writepos = 0;
567 fp->readpos = 0;
568 php_stream_seek(fp, entry.offset, SEEK_SET);
569 fp->writepos = 0;
570 fp->readpos = 0;
571 /* the above lines should be for php < 5.2.6 after 5.3 filters are fixed */
572
573 mydata->alias_len = entry.uncompressed_filesize;
574 if (entry.flags & PHAR_ENT_COMPRESSED_GZ) {
575 filter = php_stream_filter_create("zlib.inflate", NULL, php_stream_is_persistent(fp));
576
577 if (!filter) {
578 pefree(entry.filename, entry.is_persistent);
579 PHAR_ZIP_FAIL("unable to decompress alias, zlib filter creation failed");
580 }
581
582 php_stream_filter_append(&fp->readfilters, filter);
583
584 // TODO: refactor to avoid reallocation ???
585 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
586 {
587 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
588 if (str) {
589 entry.uncompressed_filesize = ZSTR_LEN(str);
590 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
591 zend_string_release(str);
592 } else {
593 actual_alias = NULL;
594 entry.uncompressed_filesize = 0;
595 }
596 }
597
598 if (!entry.uncompressed_filesize || !actual_alias) {
599 pefree(entry.filename, entry.is_persistent);
600 PHAR_ZIP_FAIL("unable to read in alias, truncated");
601 }
602
603 php_stream_filter_flush(filter, 1);
604 php_stream_filter_remove(filter, 1);
605
606 } else if (entry.flags & PHAR_ENT_COMPRESSED_BZ2) {
607 filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp));
608
609 if (!filter) {
610 pefree(entry.filename, entry.is_persistent);
611 PHAR_ZIP_FAIL("unable to read in alias, bzip2 filter creation failed");
612 }
613
614 php_stream_filter_append(&fp->readfilters, filter);
615
616 // TODO: refactor to avoid reallocation ???
617 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
618 {
619 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
620 if (str) {
621 entry.uncompressed_filesize = ZSTR_LEN(str);
622 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
623 zend_string_release(str);
624 } else {
625 actual_alias = NULL;
626 entry.uncompressed_filesize = 0;
627 }
628 }
629
630 if (!entry.uncompressed_filesize || !actual_alias) {
631 pefree(entry.filename, entry.is_persistent);
632 PHAR_ZIP_FAIL("unable to read in alias, truncated");
633 }
634
635 php_stream_filter_flush(filter, 1);
636 php_stream_filter_remove(filter, 1);
637 } else {
638 // TODO: refactor to avoid reallocation ???
639 //??? entry.uncompressed_filesize = php_stream_copy_to_mem(fp, &actual_alias, entry.uncompressed_filesize, 0)
640 {
641 zend_string *str = php_stream_copy_to_mem(fp, entry.uncompressed_filesize, 0);
642 if (str) {
643 entry.uncompressed_filesize = ZSTR_LEN(str);
644 actual_alias = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
645 zend_string_release(str);
646 } else {
647 actual_alias = NULL;
648 entry.uncompressed_filesize = 0;
649 }
650 }
651
652 if (!entry.uncompressed_filesize || !actual_alias) {
653 pefree(entry.filename, entry.is_persistent);
654 PHAR_ZIP_FAIL("unable to read in alias, truncated");
655 }
656 }
657
658 /* return to central directory parsing */
659 php_stream_seek(fp, saveloc, SEEK_SET);
660 }
661
662 phar_set_inode(&entry);
663 zend_hash_str_add_mem(&mydata->manifest, entry.filename, entry.filename_len, (void *)&entry, sizeof(phar_entry_info));
664 }
665
666 mydata->fp = fp;
667
668 if (zend_hash_str_exists(&(mydata->manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
669 mydata->is_data = 0;
670 } else {
671 mydata->is_data = 1;
672 }
673
674 zend_hash_str_add_ptr(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len, mydata);
675
676 if (actual_alias) {
677 phar_archive_data *fd_ptr;
678
679 if (!phar_validate_alias(actual_alias, mydata->alias_len)) {
680 if (error) {
681 spprintf(error, 4096, "phar error: invalid alias \"%s\" in zip-based phar \"%s\"", actual_alias, fname);
682 }
683 efree(actual_alias);
684 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
685 return FAILURE;
686 }
687
688 mydata->is_temporary_alias = 0;
689
690 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len))) {
691 if (SUCCESS != phar_free_alias(fd_ptr, actual_alias, mydata->alias_len)) {
692 if (error) {
693 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with implicit alias, alias is already in use", fname);
694 }
695 efree(actual_alias);
696 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
697 return FAILURE;
698 }
699 }
700
701 mydata->alias = entry.is_persistent ? pestrndup(actual_alias, mydata->alias_len, 1) : actual_alias;
702
703 if (entry.is_persistent) {
704 efree(actual_alias);
705 }
706
707 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len, mydata);
708 } else {
709 phar_archive_data *fd_ptr;
710
711 if (alias_len) {
712 if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
713 if (SUCCESS != phar_free_alias(fd_ptr, alias, alias_len)) {
714 if (error) {
715 spprintf(error, 4096, "phar error: Unable to add zip-based phar \"%s\" with explicit alias, alias is already in use", fname);
716 }
717 zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
718 return FAILURE;
719 }
720 }
721
722 zend_hash_str_add_ptr(&(PHAR_G(phar_alias_map)), actual_alias, mydata->alias_len, mydata);
723 mydata->alias = pestrndup(alias, alias_len, mydata->is_persistent);
724 mydata->alias_len = alias_len;
725 } else {
726 mydata->alias = pestrndup(mydata->fname, fname_len, mydata->is_persistent);
727 mydata->alias_len = fname_len;
728 }
729
730 mydata->is_temporary_alias = 1;
731 }
732
733 if (pphar) {
734 *pphar = mydata;
735 }
736
737 return SUCCESS;
738 }
739 /* }}} */
740
741 /**
742 * Create or open a zip-based phar for writing
743 */
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 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) /* {{{ */
745 {
746 phar_archive_data *phar;
747 int ret = phar_create_or_parse_filename(fname, fname_len, alias, alias_len, is_data, options, &phar, error);
748
749 if (FAILURE == ret) {
750 return FAILURE;
751 }
752
753 if (pphar) {
754 *pphar = phar;
755 }
756
757 phar->is_data = is_data;
758
759 if (phar->is_zip) {
760 return ret;
761 }
762
763 if (phar->is_brandnew) {
764 phar->internal_file_start = 0;
765 phar->is_zip = 1;
766 phar->is_tar = 0;
767 return SUCCESS;
768 }
769
770 /* we've reached here - the phar exists and is a regular phar */
771 if (error) {
772 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);
773 }
774
775 return FAILURE;
776 }
777 /* }}} */
778
779 struct _phar_zip_pass {
780 php_stream *filefp;
781 php_stream *centralfp;
782 php_stream *old;
783 int free_fp;
784 int free_ufp;
785 char **error;
786 };
787 /* 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)788 static int phar_zip_changed_apply_int(phar_entry_info *entry, void *arg) /* {{{ */
789 {
790 phar_zip_file_header local;
791 phar_zip_unix3 perms;
792 phar_zip_central_dir_file central;
793 struct _phar_zip_pass *p;
794 php_uint32 newcrc32;
795 zend_off_t offset;
796 int not_really_modified = 0;
797 p = (struct _phar_zip_pass*) arg;
798
799 if (entry->is_mounted) {
800 return ZEND_HASH_APPLY_KEEP;
801 }
802
803 if (entry->is_deleted) {
804 if (entry->fp_refcount <= 0) {
805 return ZEND_HASH_APPLY_REMOVE;
806 } else {
807 /* we can't delete this in-memory until it is closed */
808 return ZEND_HASH_APPLY_KEEP;
809 }
810 }
811
812 phar_add_virtual_dirs(entry->phar, entry->filename, entry->filename_len);
813 memset(&local, 0, sizeof(local));
814 memset(¢ral, 0, sizeof(central));
815 memset(&perms, 0, sizeof(perms));
816 strncpy(local.signature, "PK\3\4", 4);
817 strncpy(central.signature, "PK\1\2", 4);
818 PHAR_SET_16(central.extra_len, sizeof(perms));
819 PHAR_SET_16(local.extra_len, sizeof(perms));
820 perms.tag[0] = 'n';
821 perms.tag[1] = 'u';
822 PHAR_SET_16(perms.size, sizeof(perms) - 4);
823 PHAR_SET_16(perms.perms, entry->flags & PHAR_ENT_PERM_MASK);
824 {
825 php_uint32 crc = (php_uint32) ~0;
826 CRC32(crc, perms.perms[0]);
827 CRC32(crc, perms.perms[1]);
828 PHAR_SET_32(perms.crc32, ~crc);
829 }
830
831 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
832 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_DEFLATE);
833 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_DEFLATE);
834 }
835
836 if (entry->flags & PHAR_ENT_COMPRESSED_BZ2) {
837 PHAR_SET_16(central.compressed, PHAR_ZIP_COMP_BZIP2);
838 PHAR_SET_16(local.compressed, PHAR_ZIP_COMP_BZIP2);
839 }
840
841 /* do not use PHAR_GET_16 on either field of the next line */
842 phar_zip_u2d_time(entry->timestamp, local.timestamp, local.datestamp);
843 memcpy(central.timestamp, local.timestamp, sizeof(local.timestamp));
844 memcpy(central.datestamp, local.datestamp, sizeof(local.datestamp));
845 PHAR_SET_16(central.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
846 PHAR_SET_16(local.filename_len, entry->filename_len + (entry->is_dir ? 1 : 0));
847 PHAR_SET_32(central.offset, php_stream_tell(p->filefp));
848
849 /* do extra field for perms later */
850 if (entry->is_modified) {
851 php_uint32 loc;
852 php_stream_filter *filter;
853 php_stream *efp;
854
855 if (entry->is_dir) {
856 entry->is_modified = 0;
857 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp) {
858 php_stream_close(entry->fp);
859 entry->fp = NULL;
860 entry->fp_type = PHAR_FP;
861 }
862 goto continue_dir;
863 }
864
865 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
866 spprintf(p->error, 0, "unable to open file contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
867 return ZEND_HASH_APPLY_STOP;
868 }
869
870 /* we can be modified and already be compressed, such as when chmod() is executed */
871 if (entry->flags & PHAR_ENT_COMPRESSION_MASK && (entry->old_flags == entry->flags || !entry->old_flags)) {
872 not_really_modified = 1;
873 goto is_compressed;
874 }
875
876 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
877 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
878 return ZEND_HASH_APPLY_STOP;
879 }
880
881 efp = phar_get_efp(entry, 0);
882 newcrc32 = ~0;
883
884 for (loc = 0;loc < entry->uncompressed_filesize; ++loc) {
885 CRC32(newcrc32, php_stream_getc(efp));
886 }
887
888 entry->crc32 = ~newcrc32;
889 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
890 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
891
892 if (!(entry->flags & PHAR_ENT_COMPRESSION_MASK)) {
893 /* not compressed */
894 entry->compressed_filesize = entry->uncompressed_filesize;
895 PHAR_SET_32(central.compsize, entry->uncompressed_filesize);
896 PHAR_SET_32(local.compsize, entry->uncompressed_filesize);
897 goto not_compressed;
898 }
899
900 filter = php_stream_filter_create(phar_compress_filter(entry, 0), NULL, 0);
901
902 if (!filter) {
903 if (entry->flags & PHAR_ENT_COMPRESSED_GZ) {
904 spprintf(p->error, 0, "unable to gzip compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
905 } else {
906 spprintf(p->error, 0, "unable to bzip2 compress file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
907 }
908 return ZEND_HASH_APPLY_STOP;
909 }
910
911 /* create new file that holds the compressed version */
912 /* work around inability to specify freedom in write and strictness
913 in read count */
914 entry->cfp = php_stream_fopen_tmpfile();
915
916 if (!entry->cfp) {
917 spprintf(p->error, 0, "unable to create temporary file for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
918 return ZEND_HASH_APPLY_STOP;
919 }
920
921 php_stream_flush(efp);
922
923 if (-1 == phar_seek_efp(entry, 0, SEEK_SET, 0, 0)) {
924 spprintf(p->error, 0, "unable to seek to start of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
925 return ZEND_HASH_APPLY_STOP;
926 }
927
928 php_stream_filter_append((&entry->cfp->writefilters), filter);
929
930 if (SUCCESS != php_stream_copy_to_stream_ex(efp, entry->cfp, entry->uncompressed_filesize, NULL)) {
931 spprintf(p->error, 0, "unable to copy compressed file contents of file \"%s\" while creating new phar \"%s\"", entry->filename, entry->phar->fname);
932 return ZEND_HASH_APPLY_STOP;
933 }
934
935 php_stream_filter_flush(filter, 1);
936 php_stream_flush(entry->cfp);
937 php_stream_filter_remove(filter, 1);
938 php_stream_seek(entry->cfp, 0, SEEK_END);
939 entry->compressed_filesize = (php_uint32) php_stream_tell(entry->cfp);
940 PHAR_SET_32(central.compsize, entry->compressed_filesize);
941 PHAR_SET_32(local.compsize, entry->compressed_filesize);
942 /* generate crc on compressed file */
943 php_stream_rewind(entry->cfp);
944 entry->old_flags = entry->flags;
945 entry->is_modified = 1;
946 } else {
947 is_compressed:
948 PHAR_SET_32(central.uncompsize, entry->uncompressed_filesize);
949 PHAR_SET_32(local.uncompsize, entry->uncompressed_filesize);
950 PHAR_SET_32(central.compsize, entry->compressed_filesize);
951 PHAR_SET_32(local.compsize, entry->compressed_filesize);
952 if (p->old) {
953 if (-1 == php_stream_seek(p->old, entry->offset_abs, SEEK_SET)) {
954 spprintf(p->error, 0, "unable to seek to start of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
955 return ZEND_HASH_APPLY_STOP;
956 }
957 }
958 }
959 not_compressed:
960 PHAR_SET_32(central.crc32, entry->crc32);
961 PHAR_SET_32(local.crc32, entry->crc32);
962 continue_dir:
963 /* set file metadata */
964 if (Z_TYPE(entry->metadata) != IS_UNDEF) {
965 php_serialize_data_t metadata_hash;
966
967 if (entry->metadata_str.s) {
968 smart_str_free(&entry->metadata_str);
969 }
970 entry->metadata_str.s = NULL;
971 PHP_VAR_SERIALIZE_INIT(metadata_hash);
972 php_var_serialize(&entry->metadata_str, &entry->metadata, &metadata_hash);
973 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
974 PHAR_SET_16(central.comment_len, ZSTR_LEN(entry->metadata_str.s));
975 }
976
977 entry->header_offset = php_stream_tell(p->filefp);
978 offset = entry->header_offset + sizeof(local) + entry->filename_len + (entry->is_dir ? 1 : 0) + sizeof(perms);
979
980 if (sizeof(local) != php_stream_write(p->filefp, (char *)&local, sizeof(local))) {
981 spprintf(p->error, 0, "unable to write local file header of file \"%s\" to zip-based phar \"%s\"", entry->filename, entry->phar->fname);
982 return ZEND_HASH_APPLY_STOP;
983 }
984
985 if (sizeof(central) != php_stream_write(p->centralfp, (char *)¢ral, sizeof(central))) {
986 spprintf(p->error, 0, "unable to write central directory entry for file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
987 return ZEND_HASH_APPLY_STOP;
988 }
989
990 if (entry->is_dir) {
991 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
992 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);
993 return ZEND_HASH_APPLY_STOP;
994 }
995
996 if (1 != php_stream_write(p->filefp, "/", 1)) {
997 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);
998 return ZEND_HASH_APPLY_STOP;
999 }
1000
1001 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1002 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);
1003 return ZEND_HASH_APPLY_STOP;
1004 }
1005
1006 if (1 != php_stream_write(p->centralfp, "/", 1)) {
1007 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);
1008 return ZEND_HASH_APPLY_STOP;
1009 }
1010 } else {
1011 if (entry->filename_len != php_stream_write(p->filefp, entry->filename, entry->filename_len)) {
1012 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);
1013 return ZEND_HASH_APPLY_STOP;
1014 }
1015
1016 if (entry->filename_len != php_stream_write(p->centralfp, entry->filename, entry->filename_len)) {
1017 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);
1018 return ZEND_HASH_APPLY_STOP;
1019 }
1020 }
1021
1022 if (sizeof(perms) != php_stream_write(p->filefp, (char *)&perms, sizeof(perms))) {
1023 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);
1024 return ZEND_HASH_APPLY_STOP;
1025 }
1026
1027 if (sizeof(perms) != php_stream_write(p->centralfp, (char *)&perms, sizeof(perms))) {
1028 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);
1029 return ZEND_HASH_APPLY_STOP;
1030 }
1031
1032 if (!not_really_modified && entry->is_modified) {
1033 if (entry->cfp) {
1034 if (SUCCESS != php_stream_copy_to_stream_ex(entry->cfp, p->filefp, entry->compressed_filesize, NULL)) {
1035 spprintf(p->error, 0, "unable to write compressed contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1036 return ZEND_HASH_APPLY_STOP;
1037 }
1038
1039 php_stream_close(entry->cfp);
1040 entry->cfp = NULL;
1041 } else {
1042 if (FAILURE == phar_open_entry_fp(entry, p->error, 0)) {
1043 return ZEND_HASH_APPLY_STOP;
1044 }
1045
1046 phar_seek_efp(entry, 0, SEEK_SET, 0, 0);
1047
1048 if (SUCCESS != php_stream_copy_to_stream_ex(phar_get_efp(entry, 0), p->filefp, entry->uncompressed_filesize, NULL)) {
1049 spprintf(p->error, 0, "unable to write contents of file \"%s\" in zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1050 return ZEND_HASH_APPLY_STOP;
1051 }
1052 }
1053
1054 if (entry->fp_type == PHAR_MOD && entry->fp != entry->phar->fp && entry->fp != entry->phar->ufp && entry->fp_refcount == 0) {
1055 php_stream_close(entry->fp);
1056 }
1057
1058 entry->is_modified = 0;
1059 } else {
1060 entry->is_modified = 0;
1061 if (entry->fp_refcount) {
1062 /* open file pointers refer to this fp, do not free the stream */
1063 switch (entry->fp_type) {
1064 case PHAR_FP:
1065 p->free_fp = 0;
1066 break;
1067 case PHAR_UFP:
1068 p->free_ufp = 0;
1069 default:
1070 break;
1071 }
1072 }
1073
1074 if (!entry->is_dir && entry->compressed_filesize && SUCCESS != php_stream_copy_to_stream_ex(p->old, p->filefp, entry->compressed_filesize, NULL)) {
1075 spprintf(p->error, 0, "unable to copy contents of file \"%s\" while creating zip-based phar \"%s\"", entry->filename, entry->phar->fname);
1076 return ZEND_HASH_APPLY_STOP;
1077 }
1078 }
1079
1080 entry->fp = NULL;
1081 entry->offset = entry->offset_abs = offset;
1082 entry->fp_type = PHAR_FP;
1083
1084 if (entry->metadata_str.s) {
1085 if (ZSTR_LEN(entry->metadata_str.s) != php_stream_write(p->centralfp, ZSTR_VAL(entry->metadata_str.s), ZSTR_LEN(entry->metadata_str.s))) {
1086 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);
1087 smart_str_free(&entry->metadata_str);
1088 return ZEND_HASH_APPLY_STOP;
1089 }
1090
1091 smart_str_free(&entry->metadata_str);
1092 }
1093
1094 return ZEND_HASH_APPLY_KEEP;
1095 }
1096 /* }}} */
1097
phar_zip_changed_apply(zval * zv,void * arg)1098 static int phar_zip_changed_apply(zval *zv, void *arg) /* {{{ */
1099 {
1100 return phar_zip_changed_apply_int(Z_PTR_P(zv), arg);
1101 }
1102 /* }}} */
1103
phar_zip_applysignature(phar_archive_data * phar,struct _phar_zip_pass * pass,smart_str * metadata)1104 static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pass *pass,
1105 smart_str *metadata) /* {{{ */
1106 {
1107 /* add signature for executable tars or tars explicitly set with setSignatureAlgorithm */
1108 if (!phar->is_data || phar->sig_flags) {
1109 int signature_length;
1110 char *signature, sigbuf[8];
1111 phar_entry_info entry = {0};
1112 php_stream *newfile;
1113 zend_off_t tell, st;
1114
1115 newfile = php_stream_fopen_tmpfile();
1116 if (newfile == NULL) {
1117 spprintf(pass->error, 0, "phar error: unable to create temporary file for the signature file");
1118 return FAILURE;
1119 }
1120 st = tell = php_stream_tell(pass->filefp);
1121 /* copy the local files, central directory, and the zip comment to generate the hash */
1122 php_stream_seek(pass->filefp, 0, SEEK_SET);
1123 php_stream_copy_to_stream_ex(pass->filefp, newfile, tell, NULL);
1124 tell = php_stream_tell(pass->centralfp);
1125 php_stream_seek(pass->centralfp, 0, SEEK_SET);
1126 php_stream_copy_to_stream_ex(pass->centralfp, newfile, tell, NULL);
1127 if (metadata->s) {
1128 php_stream_write(newfile, ZSTR_VAL(metadata->s), ZSTR_LEN(metadata->s));
1129 }
1130
1131 if (FAILURE == phar_create_signature(phar, newfile, &signature, &signature_length, pass->error)) {
1132 if (pass->error) {
1133 char *save = *(pass->error);
1134 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar: %s", save);
1135 efree(save);
1136 }
1137
1138 php_stream_close(newfile);
1139 return FAILURE;
1140 }
1141
1142 entry.filename = ".phar/signature.bin";
1143 entry.filename_len = sizeof(".phar/signature.bin")-1;
1144 entry.fp = php_stream_fopen_tmpfile();
1145 entry.fp_type = PHAR_MOD;
1146 entry.is_modified = 1;
1147 if (entry.fp == NULL) {
1148 spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
1149 return FAILURE;
1150 }
1151
1152 PHAR_SET_32(sigbuf, phar->sig_flags);
1153 PHAR_SET_32(sigbuf + 4, signature_length);
1154
1155 if (8 != (int)php_stream_write(entry.fp, sigbuf, 8) || signature_length != (int)php_stream_write(entry.fp, signature, signature_length)) {
1156 efree(signature);
1157 if (pass->error) {
1158 spprintf(pass->error, 0, "phar error: unable to write signature to zip-based phar %s", phar->fname);
1159 }
1160
1161 php_stream_close(newfile);
1162 return FAILURE;
1163 }
1164
1165 efree(signature);
1166 entry.uncompressed_filesize = entry.compressed_filesize = signature_length + 8;
1167 entry.phar = phar;
1168 /* throw out return value and write the signature */
1169 phar_zip_changed_apply_int(&entry, (void *)pass);
1170 php_stream_close(newfile);
1171
1172 if (pass->error && *(pass->error)) {
1173 /* error is set by writeheaders */
1174 return FAILURE;
1175 }
1176 } /* signature */
1177 return SUCCESS;
1178 }
1179 /* }}} */
1180
phar_zip_flush(phar_archive_data * phar,char * user_stub,zend_long len,int defaultstub,char ** error)1181 int phar_zip_flush(phar_archive_data *phar, char *user_stub, zend_long len, int defaultstub, char **error) /* {{{ */
1182 {
1183 char *pos;
1184 smart_str main_metadata_str = {0};
1185 static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1186 char halt_stub[] = "__HALT_COMPILER();";
1187 char *tmp;
1188
1189 php_stream *stubfile, *oldfile;
1190 php_serialize_data_t metadata_hash;
1191 int free_user_stub, closeoldfile = 0;
1192 phar_entry_info entry = {0};
1193 char *temperr = NULL;
1194 struct _phar_zip_pass pass;
1195 phar_zip_dir_end eocd;
1196 php_uint32 cdir_size, cdir_offset;
1197
1198 pass.error = &temperr;
1199 entry.flags = PHAR_ENT_PERM_DEF_FILE;
1200 entry.timestamp = time(NULL);
1201 entry.is_modified = 1;
1202 entry.is_zip = 1;
1203 entry.phar = phar;
1204 entry.fp_type = PHAR_MOD;
1205
1206 if (phar->is_persistent) {
1207 if (error) {
1208 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1209 }
1210 return EOF;
1211 }
1212
1213 if (phar->is_data) {
1214 goto nostub;
1215 }
1216
1217 /* set alias */
1218 if (!phar->is_temporary_alias && phar->alias_len) {
1219 entry.fp = php_stream_fopen_tmpfile();
1220 if (entry.fp == NULL) {
1221 spprintf(error, 0, "phar error: unable to create temporary file");
1222 return EOF;
1223 }
1224 if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1225 if (error) {
1226 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1227 }
1228 return EOF;
1229 }
1230
1231 entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1232 entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1233 entry.filename_len = sizeof(".phar/alias.txt")-1;
1234
1235 if (NULL == zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1236 if (error) {
1237 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1238 }
1239 return EOF;
1240 }
1241 } else {
1242 zend_hash_str_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1243 }
1244
1245 /* register alias */
1246 if (phar->alias_len) {
1247 if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error)) {
1248 return EOF;
1249 }
1250 }
1251
1252 /* set stub */
1253 if (user_stub && !defaultstub) {
1254 if (len < 0) {
1255 /* resource passed in */
1256 if (!(php_stream_from_zval_no_verify(stubfile, (zval *)user_stub))) {
1257 if (error) {
1258 spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1259 }
1260 return EOF;
1261 }
1262
1263 if (len == -1) {
1264 len = PHP_STREAM_COPY_ALL;
1265 } else {
1266 len = -len;
1267 }
1268
1269 user_stub = 0;
1270
1271 // TODO: refactor to avoid reallocation ???
1272 //??? len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)
1273 {
1274 zend_string *str = php_stream_copy_to_mem(stubfile, len, 0);
1275 if (str) {
1276 len = ZSTR_LEN(str);
1277 user_stub = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
1278 zend_string_release(str);
1279 } else {
1280 user_stub = NULL;
1281 len = 0;
1282 }
1283 }
1284
1285 if (!len || !user_stub) {
1286 if (error) {
1287 spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1288 }
1289 return EOF;
1290 }
1291 free_user_stub = 1;
1292 } else {
1293 free_user_stub = 0;
1294 }
1295
1296 tmp = estrndup(user_stub, len);
1297 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1298 efree(tmp);
1299 if (error) {
1300 spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1301 }
1302 if (free_user_stub) {
1303 efree(user_stub);
1304 }
1305 return EOF;
1306 }
1307 pos = user_stub + (pos - tmp);
1308 efree(tmp);
1309
1310 len = pos - user_stub + 18;
1311 entry.fp = php_stream_fopen_tmpfile();
1312 if (entry.fp == NULL) {
1313 spprintf(error, 0, "phar error: unable to create temporary file");
1314 return EOF;
1315 }
1316 entry.uncompressed_filesize = len + 5;
1317
1318 if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1319 || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1320 if (error) {
1321 spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1322 }
1323 if (free_user_stub) {
1324 efree(user_stub);
1325 }
1326 php_stream_close(entry.fp);
1327 return EOF;
1328 }
1329
1330 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1331 entry.filename_len = sizeof(".phar/stub.php")-1;
1332
1333 if (NULL == zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1334 if (free_user_stub) {
1335 efree(user_stub);
1336 }
1337 if (error) {
1338 spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
1339 }
1340 return EOF;
1341 }
1342
1343 if (free_user_stub) {
1344 efree(user_stub);
1345 }
1346 } else {
1347 /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1348 entry.fp = php_stream_fopen_tmpfile();
1349 if (entry.fp == NULL) {
1350 spprintf(error, 0, "phar error: unable to create temporary file");
1351 return EOF;
1352 }
1353 if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1354 php_stream_close(entry.fp);
1355 if (error) {
1356 spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1357 }
1358 return EOF;
1359 }
1360
1361 entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1362 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1363 entry.filename_len = sizeof(".phar/stub.php")-1;
1364
1365 if (!defaultstub) {
1366 if (!zend_hash_str_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1367 if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1368 php_stream_close(entry.fp);
1369 efree(entry.filename);
1370 if (error) {
1371 spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1372 }
1373 return EOF;
1374 }
1375 } else {
1376 php_stream_close(entry.fp);
1377 efree(entry.filename);
1378 }
1379 } else {
1380 if (NULL == zend_hash_str_update_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
1381 php_stream_close(entry.fp);
1382 efree(entry.filename);
1383 if (error) {
1384 spprintf(error, 0, "unable to overwrite stub in zip-based phar \"%s\"", phar->fname);
1385 }
1386 return EOF;
1387 }
1388 }
1389 }
1390 nostub:
1391 if (phar->fp && !phar->is_brandnew) {
1392 oldfile = phar->fp;
1393 closeoldfile = 0;
1394 php_stream_rewind(oldfile);
1395 } else {
1396 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1397 closeoldfile = oldfile != NULL;
1398 }
1399
1400 /* save modified files to the zip */
1401 pass.old = oldfile;
1402 pass.filefp = php_stream_fopen_tmpfile();
1403
1404 if (!pass.filefp) {
1405 fperror:
1406 if (closeoldfile) {
1407 php_stream_close(oldfile);
1408 }
1409 if (error) {
1410 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1411 }
1412 return EOF;
1413 }
1414
1415 pass.centralfp = php_stream_fopen_tmpfile();
1416
1417 if (!pass.centralfp) {
1418 goto fperror;
1419 }
1420
1421 pass.free_fp = pass.free_ufp = 1;
1422 memset(&eocd, 0, sizeof(eocd));
1423
1424 strncpy(eocd.signature, "PK\5\6", 4);
1425 if (!phar->is_data && !phar->sig_flags) {
1426 phar->sig_flags = PHAR_SIG_SHA1;
1427 }
1428 if (phar->sig_flags) {
1429 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1430 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1431 } else {
1432 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1433 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1434 }
1435 zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass);
1436
1437 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1438 /* set phar metadata */
1439 PHP_VAR_SERIALIZE_INIT(metadata_hash);
1440 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash);
1441 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1442 }
1443 if (temperr) {
1444 if (error) {
1445 spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1446 }
1447 efree(temperr);
1448 temperror:
1449 php_stream_close(pass.centralfp);
1450 nocentralerror:
1451 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1452 smart_str_free(&main_metadata_str);
1453 }
1454 php_stream_close(pass.filefp);
1455 if (closeoldfile) {
1456 php_stream_close(oldfile);
1457 }
1458 return EOF;
1459 }
1460
1461 if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str)) {
1462 goto temperror;
1463 }
1464
1465 /* save zip */
1466 cdir_size = php_stream_tell(pass.centralfp);
1467 cdir_offset = php_stream_tell(pass.filefp);
1468 PHAR_SET_32(eocd.cdir_size, cdir_size);
1469 PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1470 php_stream_seek(pass.centralfp, 0, SEEK_SET);
1471
1472 {
1473 size_t clen;
1474 int ret = php_stream_copy_to_stream_ex(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1475 if (SUCCESS != ret || clen != cdir_size) {
1476 if (error) {
1477 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1478 }
1479 goto temperror;
1480 }
1481 }
1482
1483 php_stream_close(pass.centralfp);
1484
1485 if (Z_TYPE(phar->metadata) != IS_UNDEF) {
1486 /* set phar metadata */
1487 PHAR_SET_16(eocd.comment_len, ZSTR_LEN(main_metadata_str.s));
1488
1489 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1490 if (error) {
1491 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1492 }
1493 goto nocentralerror;
1494 }
1495
1496 if (ZSTR_LEN(main_metadata_str.s) != php_stream_write(pass.filefp, ZSTR_VAL(main_metadata_str.s), ZSTR_LEN(main_metadata_str.s))) {
1497 if (error) {
1498 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1499 }
1500 goto nocentralerror;
1501 }
1502
1503 smart_str_free(&main_metadata_str);
1504
1505 } else {
1506 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1507 if (error) {
1508 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1509 }
1510 goto nocentralerror;
1511 }
1512 }
1513
1514 if (phar->fp && pass.free_fp) {
1515 php_stream_close(phar->fp);
1516 }
1517
1518 if (phar->ufp) {
1519 if (pass.free_ufp) {
1520 php_stream_close(phar->ufp);
1521 }
1522 phar->ufp = NULL;
1523 }
1524
1525 /* re-open */
1526 phar->is_brandnew = 0;
1527
1528 if (phar->donotflush) {
1529 /* deferred flush */
1530 phar->fp = pass.filefp;
1531 } else {
1532 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1533 if (!phar->fp) {
1534 if (closeoldfile) {
1535 php_stream_close(oldfile);
1536 }
1537 phar->fp = pass.filefp;
1538 if (error) {
1539 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1540 }
1541 return EOF;
1542 }
1543 php_stream_rewind(pass.filefp);
1544 php_stream_copy_to_stream_ex(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1545 /* we could also reopen the file in "rb" mode but there is no need for that */
1546 php_stream_close(pass.filefp);
1547 }
1548
1549 if (closeoldfile) {
1550 php_stream_close(oldfile);
1551 }
1552 return EOF;
1553 }
1554 /* }}} */
1555
1556 /*
1557 * Local variables:
1558 * tab-width: 4
1559 * c-basic-offset: 4
1560 * End:
1561 * vim600: noet sw=4 ts=4 fdm=marker
1562 * vim<600: noet sw=4 ts=4
1563 */
1564