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