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