1 /*
2 +----------------------------------------------------------------------+
3 | ZIP archive support for Phar |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2007-2015 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 phar_stream_copy_to_stream(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 phar_stream_copy_to_stream(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) {
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 != phar_stream_copy_to_stream(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 != phar_stream_copy_to_stream(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 != phar_stream_copy_to_stream(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 != phar_stream_copy_to_stream(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 phar_stream_copy_to_stream(pass->filefp, newfile, tell, NULL);
1099 tell = php_stream_tell(pass->centralfp);
1100 php_stream_seek(pass->centralfp, 0, SEEK_SET);
1101 phar_stream_copy_to_stream(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 php_stream_close(newfile);
1150 return FAILURE;
1151 }
1152 } /* signature */
1153 return SUCCESS;
1154 }
1155 /* }}} */
1156
phar_zip_flush(phar_archive_data * phar,char * user_stub,long len,int defaultstub,char ** error TSRMLS_DC)1157 int phar_zip_flush(phar_archive_data *phar, char *user_stub, long len, int defaultstub, char **error TSRMLS_DC) /* {{{ */
1158 {
1159 char *pos;
1160 smart_str main_metadata_str = {0};
1161 static const char newstub[] = "<?php // zip-based phar archive stub file\n__HALT_COMPILER();";
1162 char halt_stub[] = "__HALT_COMPILER();";
1163 char *tmp;
1164
1165 php_stream *stubfile, *oldfile;
1166 php_serialize_data_t metadata_hash;
1167 int free_user_stub, closeoldfile = 0;
1168 phar_entry_info entry = {0};
1169 char *temperr = NULL;
1170 struct _phar_zip_pass pass;
1171 phar_zip_dir_end eocd;
1172 php_uint32 cdir_size, cdir_offset;
1173
1174 pass.error = &temperr;
1175 entry.flags = PHAR_ENT_PERM_DEF_FILE;
1176 entry.timestamp = time(NULL);
1177 entry.is_modified = 1;
1178 entry.is_zip = 1;
1179 entry.phar = phar;
1180 entry.fp_type = PHAR_MOD;
1181
1182 if (phar->is_persistent) {
1183 if (error) {
1184 spprintf(error, 0, "internal error: attempt to flush cached zip-based phar \"%s\"", phar->fname);
1185 }
1186 return EOF;
1187 }
1188
1189 if (phar->is_data) {
1190 goto nostub;
1191 }
1192
1193 /* set alias */
1194 if (!phar->is_temporary_alias && phar->alias_len) {
1195 entry.fp = php_stream_fopen_tmpfile();
1196 if (entry.fp == NULL) {
1197 spprintf(error, 0, "phar error: unable to create temporary file");
1198 return EOF;
1199 }
1200 if (phar->alias_len != (int)php_stream_write(entry.fp, phar->alias, phar->alias_len)) {
1201 if (error) {
1202 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1203 }
1204 return EOF;
1205 }
1206
1207 entry.uncompressed_filesize = entry.compressed_filesize = phar->alias_len;
1208 entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1209 entry.filename_len = sizeof(".phar/alias.txt")-1;
1210
1211 if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1212 if (error) {
1213 spprintf(error, 0, "unable to set alias in zip-based phar \"%s\"", phar->fname);
1214 }
1215 return EOF;
1216 }
1217 } else {
1218 zend_hash_del(&phar->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
1219 }
1220
1221 /* register alias */
1222 if (phar->alias_len) {
1223 if (FAILURE == phar_get_archive(&phar, phar->fname, phar->fname_len, phar->alias, phar->alias_len, error TSRMLS_CC)) {
1224 return EOF;
1225 }
1226 }
1227
1228 /* set stub */
1229 if (user_stub && !defaultstub) {
1230 if (len < 0) {
1231 /* resource passed in */
1232 if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) {
1233 if (error) {
1234 spprintf(error, 0, "unable to access resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1235 }
1236 return EOF;
1237 }
1238
1239 if (len == -1) {
1240 len = PHP_STREAM_COPY_ALL;
1241 } else {
1242 len = -len;
1243 }
1244
1245 user_stub = 0;
1246
1247 if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) {
1248 if (error) {
1249 spprintf(error, 0, "unable to read resource to copy stub to new zip-based phar \"%s\"", phar->fname);
1250 }
1251 return EOF;
1252 }
1253 free_user_stub = 1;
1254 } else {
1255 free_user_stub = 0;
1256 }
1257
1258 tmp = estrndup(user_stub, len);
1259 if ((pos = php_stristr(tmp, halt_stub, len, sizeof(halt_stub) - 1)) == NULL) {
1260 efree(tmp);
1261 if (error) {
1262 spprintf(error, 0, "illegal stub for zip-based phar \"%s\"", phar->fname);
1263 }
1264 if (free_user_stub) {
1265 efree(user_stub);
1266 }
1267 return EOF;
1268 }
1269 pos = user_stub + (pos - tmp);
1270 efree(tmp);
1271
1272 len = pos - user_stub + 18;
1273 entry.fp = php_stream_fopen_tmpfile();
1274 if (entry.fp == NULL) {
1275 spprintf(error, 0, "phar error: unable to create temporary file");
1276 return EOF;
1277 }
1278 entry.uncompressed_filesize = len + 5;
1279
1280 if ((size_t)len != php_stream_write(entry.fp, user_stub, len)
1281 || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) {
1282 if (error) {
1283 spprintf(error, 0, "unable to create stub from string in new zip-based phar \"%s\"", phar->fname);
1284 }
1285 if (free_user_stub) {
1286 efree(user_stub);
1287 }
1288 php_stream_close(entry.fp);
1289 return EOF;
1290 }
1291
1292 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1293 entry.filename_len = sizeof(".phar/stub.php")-1;
1294
1295 if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1296 if (free_user_stub) {
1297 efree(user_stub);
1298 }
1299 if (error) {
1300 spprintf(error, 0, "unable to set stub in zip-based phar \"%s\"", phar->fname);
1301 }
1302 return EOF;
1303 }
1304
1305 if (free_user_stub) {
1306 efree(user_stub);
1307 }
1308 } else {
1309 /* Either this is a brand new phar (add the stub), or the default stub is required (overwrite the stub) */
1310 entry.fp = php_stream_fopen_tmpfile();
1311 if (entry.fp == NULL) {
1312 spprintf(error, 0, "phar error: unable to create temporary file");
1313 return EOF;
1314 }
1315 if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) {
1316 php_stream_close(entry.fp);
1317 if (error) {
1318 spprintf(error, 0, "unable to %s stub in%szip-based phar \"%s\", failed", user_stub ? "overwrite" : "create", user_stub ? " " : " new ", phar->fname);
1319 }
1320 return EOF;
1321 }
1322
1323 entry.uncompressed_filesize = entry.compressed_filesize = sizeof(newstub) - 1;
1324 entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1);
1325 entry.filename_len = sizeof(".phar/stub.php")-1;
1326
1327 if (!defaultstub) {
1328 if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
1329 if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1330 php_stream_close(entry.fp);
1331 efree(entry.filename);
1332 if (error) {
1333 spprintf(error, 0, "unable to create stub in zip-based phar \"%s\"", phar->fname);
1334 }
1335 return EOF;
1336 }
1337 } else {
1338 php_stream_close(entry.fp);
1339 efree(entry.filename);
1340 }
1341 } else {
1342 if (SUCCESS != zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
1343 php_stream_close(entry.fp);
1344 efree(entry.filename);
1345 if (error) {
1346 spprintf(error, 0, "unable to overwrite stub in zip-based phar \"%s\"", phar->fname);
1347 }
1348 return EOF;
1349 }
1350 }
1351 }
1352 nostub:
1353 if (phar->fp && !phar->is_brandnew) {
1354 oldfile = phar->fp;
1355 closeoldfile = 0;
1356 php_stream_rewind(oldfile);
1357 } else {
1358 oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL);
1359 closeoldfile = oldfile != NULL;
1360 }
1361
1362 /* save modified files to the zip */
1363 pass.old = oldfile;
1364 pass.filefp = php_stream_fopen_tmpfile();
1365
1366 if (!pass.filefp) {
1367 fperror:
1368 if (closeoldfile) {
1369 php_stream_close(oldfile);
1370 }
1371 if (error) {
1372 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to open temporary file", phar->fname);
1373 }
1374 return EOF;
1375 }
1376
1377 pass.centralfp = php_stream_fopen_tmpfile();
1378
1379 if (!pass.centralfp) {
1380 goto fperror;
1381 }
1382
1383 pass.free_fp = pass.free_ufp = 1;
1384 memset(&eocd, 0, sizeof(eocd));
1385
1386 strncpy(eocd.signature, "PK\5\6", 4);
1387 if (!phar->is_data && !phar->sig_flags) {
1388 phar->sig_flags = PHAR_SIG_SHA1;
1389 }
1390 if (phar->sig_flags) {
1391 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest) + 1);
1392 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest) + 1);
1393 } else {
1394 PHAR_SET_16(eocd.counthere, zend_hash_num_elements(&phar->manifest));
1395 PHAR_SET_16(eocd.count, zend_hash_num_elements(&phar->manifest));
1396 }
1397 zend_hash_apply_with_argument(&phar->manifest, phar_zip_changed_apply, (void *) &pass TSRMLS_CC);
1398
1399 if (phar->metadata) {
1400 /* set phar metadata */
1401 PHP_VAR_SERIALIZE_INIT(metadata_hash);
1402 php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC);
1403 PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
1404 }
1405 if (temperr) {
1406 if (error) {
1407 spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
1408 }
1409 efree(temperr);
1410 temperror:
1411 php_stream_close(pass.centralfp);
1412 nocentralerror:
1413 if (phar->metadata) {
1414 smart_str_free(&main_metadata_str);
1415 }
1416 php_stream_close(pass.filefp);
1417 if (closeoldfile) {
1418 php_stream_close(oldfile);
1419 }
1420 return EOF;
1421 }
1422
1423 if (FAILURE == phar_zip_applysignature(phar, &pass, &main_metadata_str TSRMLS_CC)) {
1424 goto temperror;
1425 }
1426
1427 /* save zip */
1428 cdir_size = php_stream_tell(pass.centralfp);
1429 cdir_offset = php_stream_tell(pass.filefp);
1430 PHAR_SET_32(eocd.cdir_size, cdir_size);
1431 PHAR_SET_32(eocd.cdir_offset, cdir_offset);
1432 php_stream_seek(pass.centralfp, 0, SEEK_SET);
1433
1434 {
1435 size_t clen;
1436 int ret = phar_stream_copy_to_stream(pass.centralfp, pass.filefp, PHP_STREAM_COPY_ALL, &clen);
1437 if (SUCCESS != ret || clen != cdir_size) {
1438 if (error) {
1439 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
1440 }
1441 goto temperror;
1442 }
1443 }
1444
1445 php_stream_close(pass.centralfp);
1446
1447 if (phar->metadata) {
1448 /* set phar metadata */
1449 PHAR_SET_16(eocd.comment_len, main_metadata_str.len);
1450
1451 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1452 if (error) {
1453 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1454 }
1455 goto nocentralerror;
1456 }
1457
1458 if (main_metadata_str.len != php_stream_write(pass.filefp, main_metadata_str.c, main_metadata_str.len)) {
1459 if (error) {
1460 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write metadata to zip comment", phar->fname);
1461 }
1462 goto nocentralerror;
1463 }
1464
1465 smart_str_free(&main_metadata_str);
1466
1467 } else {
1468 if (sizeof(eocd) != php_stream_write(pass.filefp, (char *)&eocd, sizeof(eocd))) {
1469 if (error) {
1470 spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write end of central-directory", phar->fname);
1471 }
1472 goto nocentralerror;
1473 }
1474 }
1475
1476 if (phar->fp && pass.free_fp) {
1477 php_stream_close(phar->fp);
1478 }
1479
1480 if (phar->ufp) {
1481 if (pass.free_ufp) {
1482 php_stream_close(phar->ufp);
1483 }
1484 phar->ufp = NULL;
1485 }
1486
1487 /* re-open */
1488 phar->is_brandnew = 0;
1489
1490 if (phar->donotflush) {
1491 /* deferred flush */
1492 phar->fp = pass.filefp;
1493 } else {
1494 phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL);
1495 if (!phar->fp) {
1496 if (closeoldfile) {
1497 php_stream_close(oldfile);
1498 }
1499 phar->fp = pass.filefp;
1500 if (error) {
1501 spprintf(error, 4096, "unable to open new phar \"%s\" for writing", phar->fname);
1502 }
1503 return EOF;
1504 }
1505 php_stream_rewind(pass.filefp);
1506 phar_stream_copy_to_stream(pass.filefp, phar->fp, PHP_STREAM_COPY_ALL, NULL);
1507 /* we could also reopen the file in "rb" mode but there is no need for that */
1508 php_stream_close(pass.filefp);
1509 }
1510
1511 if (closeoldfile) {
1512 php_stream_close(oldfile);
1513 }
1514 return EOF;
1515 }
1516 /* }}} */
1517
1518 /*
1519 * Local variables:
1520 * tab-width: 4
1521 * c-basic-offset: 4
1522 * End:
1523 * vim600: noet sw=4 ts=4 fdm=marker
1524 * vim<600: noet sw=4 ts=4
1525 */
1526