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