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