1 : /*
2 : +----------------------------------------------------------------------+
3 : | phar:// stream wrapper support |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 2005-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 : | Marcus Boerger <helly@php.net> |
17 : +----------------------------------------------------------------------+
18 : */
19 :
20 : #define PHAR_STREAM 1
21 : #include "phar_internal.h"
22 : #include "stream.h"
23 : #include "dirstream.h"
24 :
25 : php_stream_ops phar_ops = {
26 : phar_stream_write, /* write */
27 : phar_stream_read, /* read */
28 : phar_stream_close, /* close */
29 : phar_stream_flush, /* flush */
30 : "phar stream",
31 : phar_stream_seek, /* seek */
32 : NULL, /* cast */
33 : phar_stream_stat, /* stat */
34 : NULL, /* set option */
35 : };
36 :
37 : php_stream_wrapper_ops phar_stream_wops = {
38 : phar_wrapper_open_url,
39 : NULL, /* phar_wrapper_close */
40 : NULL, /* phar_wrapper_stat, */
41 : phar_wrapper_stat, /* stat_url */
42 : phar_wrapper_open_dir, /* opendir */
43 : "phar",
44 : phar_wrapper_unlink, /* unlink */
45 : phar_wrapper_rename, /* rename */
46 : phar_wrapper_mkdir, /* create directory */
47 : phar_wrapper_rmdir, /* remove directory */
48 : };
49 :
50 : php_stream_wrapper php_stream_phar_wrapper = {
51 : &phar_stream_wops,
52 : NULL,
53 : 0 /* is_url */
54 : };
55 :
56 : /**
57 : * Open a phar file for streams API
58 : */
59 : php_url* phar_parse_url(php_stream_wrapper *wrapper, char *filename, char *mode, int options TSRMLS_DC) /* {{{ */
60 1540 : {
61 : php_url *resource;
62 1540 : char *arch = NULL, *entry = NULL, *error;
63 : int arch_len, entry_len;
64 :
65 1540 : if (strlen(filename) < 7 || strncasecmp(filename, "phar://", 7)) {
66 0 : return NULL;
67 : }
68 1540 : if (mode[0] == 'a') {
69 1 : if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
70 1 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: open mode append not supported");
71 : }
72 1 : return NULL;
73 : }
74 1539 : if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0) TSRMLS_CC) == FAILURE) {
75 19 : if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
76 18 : if (arch && !entry) {
77 5 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch);
78 5 : arch = NULL;
79 : } else {
80 8 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url or non-existent phar \"%s\"", filename);
81 : }
82 : }
83 19 : return NULL;
84 : }
85 1520 : resource = ecalloc(1, sizeof(php_url));
86 1520 : resource->scheme = estrndup("phar", 4);
87 1520 : resource->host = arch;
88 :
89 1520 : resource->path = entry;
90 : #if MBO_0
91 : if (resource) {
92 : fprintf(stderr, "Alias: %s\n", alias);
93 : fprintf(stderr, "Scheme: %s\n", resource->scheme);
94 : /* fprintf(stderr, "User: %s\n", resource->user);*/
95 : /* fprintf(stderr, "Pass: %s\n", resource->pass ? "***" : NULL);*/
96 : fprintf(stderr, "Host: %s\n", resource->host);
97 : /* fprintf(stderr, "Port: %d\n", resource->port);*/
98 : fprintf(stderr, "Path: %s\n", resource->path);
99 : /* fprintf(stderr, "Query: %s\n", resource->query);*/
100 : /* fprintf(stderr, "Fragment: %s\n", resource->fragment);*/
101 : }
102 : #endif
103 1624 : if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
104 122 : phar_archive_data **pphar = NULL, *phar;
105 :
106 122 : if (PHAR_GLOBALS->request_init && PHAR_GLOBALS->phar_fname_map.arBuckets && FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), arch, arch_len, (void **)&pphar)) {
107 39 : pphar = NULL;
108 : }
109 122 : if (PHAR_G(readonly) && (!pphar || !(*pphar)->is_data)) {
110 16 : if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
111 15 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
112 : }
113 16 : php_url_free(resource);
114 16 : return NULL;
115 : }
116 106 : if (phar_open_or_create_filename(resource->host, arch_len, NULL, 0, 0, options, &phar, &error TSRMLS_CC) == FAILURE)
117 : {
118 2 : if (error) {
119 2 : if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
120 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
121 : }
122 2 : efree(error);
123 : }
124 2 : php_url_free(resource);
125 2 : return NULL;
126 : }
127 104 : if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
128 0 : if (error) {
129 0 : spprintf(&error, 0, "Cannot open cached phar '%s' as writeable, copy on write failed", resource->host);
130 0 : if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
131 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
132 : }
133 0 : efree(error);
134 : }
135 0 : php_url_free(resource);
136 0 : return NULL;
137 : }
138 : } else {
139 1398 : if (phar_open_from_filename(resource->host, arch_len, NULL, 0, options, NULL, &error TSRMLS_CC) == FAILURE)
140 : {
141 9 : if (error) {
142 7 : if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
143 6 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
144 : }
145 7 : efree(error);
146 : }
147 9 : php_url_free(resource);
148 9 : return NULL;
149 : }
150 : }
151 1493 : return resource;
152 : }
153 : /* }}} */
154 :
155 : /**
156 : * used for fopen('phar://...') and company
157 : */
158 : static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
159 614 : {
160 : phar_archive_data *phar;
161 : phar_entry_data *idata;
162 : char *internal_file;
163 : char *error;
164 : HashTable *pharcontext;
165 614 : php_url *resource = NULL;
166 : php_stream *fpf;
167 : zval **pzoption, *metadata;
168 : uint host_len;
169 :
170 614 : if ((resource = phar_parse_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
171 27 : return NULL;
172 : }
173 :
174 : /* we must have at the very least phar://alias.phar/internalfile.php */
175 587 : if (!resource->scheme || !resource->host || !resource->path) {
176 0 : php_url_free(resource);
177 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", path);
178 0 : return NULL;
179 : }
180 :
181 587 : if (strcasecmp("phar", resource->scheme)) {
182 0 : php_url_free(resource);
183 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", path);
184 0 : return NULL;
185 : }
186 :
187 587 : host_len = strlen(resource->host);
188 587 : phar_request_initialize(TSRMLS_C);
189 :
190 : /* strip leading "/" */
191 587 : internal_file = estrdup(resource->path + 1);
192 587 : if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) {
193 51 : if (NULL == (idata = phar_get_or_create_entry_data(resource->host, host_len, internal_file, strlen(internal_file), mode, 0, &error, 1 TSRMLS_CC))) {
194 4 : if (error) {
195 4 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
196 4 : efree(error);
197 : } else {
198 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: file \"%s\" could not be created in phar \"%s\"", internal_file, resource->host);
199 : }
200 4 : efree(internal_file);
201 4 : php_url_free(resource);
202 4 : return NULL;
203 : }
204 47 : if (error) {
205 0 : efree(error);
206 : }
207 47 : fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
208 47 : php_url_free(resource);
209 47 : efree(internal_file);
210 : #if PHP_MAJOR_VERSION >= 6
211 : if (context && context->options && phar_find_key(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption TSRMLS_CC)) {
212 : #else
213 47 : if (context && context->options && zend_hash_find(HASH_OF(context->options), "phar", sizeof("phar"), (void**)&pzoption) == SUCCESS) {
214 : #endif
215 7 : pharcontext = HASH_OF(*pzoption);
216 7 : if (idata->internal_file->uncompressed_filesize == 0
217 : && idata->internal_file->compressed_filesize == 0
218 : #if PHP_MAJOR_VERSION >= 6
219 : && phar_find_key(pharcontext, "compress", sizeof("compress"), (void**)&pzoption TSRMLS_CC)
220 : #else
221 : && zend_hash_find(pharcontext, "compress", sizeof("compress"), (void**)&pzoption) == SUCCESS
222 : #endif
223 : && Z_TYPE_PP(pzoption) == IS_LONG
224 : && (Z_LVAL_PP(pzoption) & ~PHAR_ENT_COMPRESSION_MASK) == 0
225 : ) {
226 5 : idata->internal_file->flags &= ~PHAR_ENT_COMPRESSION_MASK;
227 5 : idata->internal_file->flags |= Z_LVAL_PP(pzoption);
228 : }
229 : #if PHP_MAJOR_VERSION >= 6
230 : if (phar_find_key(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption TSRMLS_CC)) {
231 : #else
232 7 : if (zend_hash_find(pharcontext, "metadata", sizeof("metadata"), (void**)&pzoption) == SUCCESS) {
233 : #endif
234 3 : if (idata->internal_file->metadata) {
235 1 : zval_ptr_dtor(&idata->internal_file->metadata);
236 1 : idata->internal_file->metadata = NULL;
237 : }
238 :
239 3 : MAKE_STD_ZVAL(idata->internal_file->metadata);
240 3 : metadata = *pzoption;
241 3 : ZVAL_ZVAL(idata->internal_file->metadata, metadata, 1, 0);
242 3 : idata->phar->is_modified = 1;
243 : }
244 : }
245 47 : if (opened_path) {
246 0 : spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename);
247 : }
248 47 : return fpf;
249 : } else {
250 536 : if (!*internal_file && (options & STREAM_OPEN_FOR_INCLUDE)) {
251 : /* retrieve the stub */
252 2 : if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, NULL TSRMLS_CC)) {
253 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "file %s is not a valid phar archive");
254 0 : efree(internal_file);
255 0 : php_url_free(resource);
256 0 : return NULL;
257 : }
258 2 : if (phar->is_tar || phar->is_zip) {
259 1 : if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, ".phar/stub.php", sizeof(".phar/stub.php")-1, "r", 0, &error, 0 TSRMLS_CC)) || !idata) {
260 : goto idata_error;
261 : }
262 1 : efree(internal_file);
263 1 : if (opened_path) {
264 1 : spprintf(opened_path, MAXPATHLEN, "%s", phar->fname);
265 : }
266 1 : php_url_free(resource);
267 1 : goto phar_stub;
268 : } else {
269 : phar_entry_info *entry;
270 :
271 1 : entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
272 1 : entry->is_temp_dir = 1;
273 1 : entry->filename = estrndup("", 0);
274 1 : entry->filename_len = 0;
275 1 : entry->phar = phar;
276 1 : entry->offset = entry->offset_abs = 0;
277 1 : entry->compressed_filesize = entry->uncompressed_filesize = phar->halt_offset;
278 1 : entry->is_crc_checked = 1;
279 :
280 1 : idata = (phar_entry_data *) ecalloc(1, sizeof(phar_entry_data));
281 1 : idata->fp = phar_get_pharfp(phar TSRMLS_CC);
282 1 : idata->phar = phar;
283 1 : idata->internal_file = entry;
284 1 : if (!phar->is_persistent) {
285 1 : ++(entry->phar->refcount);
286 : }
287 1 : ++(entry->fp_refcount);
288 1 : php_url_free(resource);
289 1 : if (opened_path) {
290 1 : spprintf(opened_path, MAXPATHLEN, "%s", phar->fname);
291 : }
292 1 : efree(internal_file);
293 1 : goto phar_stub;
294 : }
295 : }
296 : /* read-only access is allowed to magic files in .phar directory */
297 534 : if ((FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, strlen(internal_file), "r", 0, &error, 0 TSRMLS_CC)) || !idata) {
298 29 : idata_error:
299 29 : if (error) {
300 8 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
301 8 : efree(error);
302 : } else {
303 21 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" is not a file in phar \"%s\"", internal_file, resource->host);
304 : }
305 29 : efree(internal_file);
306 29 : php_url_free(resource);
307 29 : return NULL;
308 : }
309 : }
310 505 : php_url_free(resource);
311 : #if MBO_0
312 : fprintf(stderr, "Pharname: %s\n", idata->phar->filename);
313 : fprintf(stderr, "Filename: %s\n", internal_file);
314 : fprintf(stderr, "Entry: %s\n", idata->internal_file->filename);
315 : fprintf(stderr, "Size: %u\n", idata->internal_file->uncompressed_filesize);
316 : fprintf(stderr, "Compressed: %u\n", idata->internal_file->flags);
317 : fprintf(stderr, "Offset: %u\n", idata->internal_file->offset_within_phar);
318 : fprintf(stderr, "Cached: %s\n", idata->internal_file->filedata ? "yes" : "no");
319 : #endif
320 :
321 : /* check length, crc32 */
322 505 : if (!idata->internal_file->is_crc_checked && phar_postprocess_file(idata, idata->internal_file->crc32, &error, 2 TSRMLS_CC) != SUCCESS) {
323 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
324 2 : efree(error);
325 2 : phar_entry_delref(idata TSRMLS_CC);
326 2 : efree(internal_file);
327 2 : return NULL;
328 : }
329 :
330 503 : if (!PHAR_G(cwd_init) && options & STREAM_OPEN_FOR_INCLUDE) {
331 137 : char *entry = idata->internal_file->filename, *cwd;
332 :
333 137 : PHAR_G(cwd_init) = 1;
334 188 : if ((idata->phar->is_tar || idata->phar->is_zip) && idata->internal_file->filename_len == sizeof(".phar/stub.php")-1 && !strncmp(idata->internal_file->filename, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
335 : /* we're executing the stub, which doesn't count as a file */
336 51 : PHAR_G(cwd_init) = 0;
337 86 : } else if ((cwd = strrchr(entry, '/'))) {
338 28 : PHAR_G(cwd_len) = cwd - entry;
339 28 : PHAR_G(cwd) = estrndup(entry, PHAR_G(cwd_len));
340 : } else {
341 : /* root directory */
342 58 : PHAR_G(cwd_len) = 0;
343 58 : PHAR_G(cwd) = NULL;
344 : }
345 : }
346 503 : if (opened_path) {
347 266 : spprintf(opened_path, MAXPATHLEN, "phar://%s/%s", idata->phar->fname, idata->internal_file->filename);
348 : }
349 503 : efree(internal_file);
350 505 : phar_stub:
351 505 : fpf = php_stream_alloc(&phar_ops, idata, NULL, mode);
352 505 : return fpf;
353 : }
354 : /* }}} */
355 :
356 : /**
357 : * Used for fclose($fp) where $fp is a phar archive
358 : */
359 : static int phar_stream_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
360 552 : {
361 552 : phar_entry_delref((phar_entry_data *)stream->abstract TSRMLS_CC);
362 :
363 552 : return 0;
364 : }
365 : /* }}} */
366 :
367 : /**
368 : * used for fread($fp) and company on a fopen()ed phar file handle
369 : */
370 : static size_t phar_stream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
371 974 : {
372 974 : phar_entry_data *data = (phar_entry_data *)stream->abstract;
373 : size_t got;
374 : phar_entry_info *entry;
375 :
376 974 : if (data->internal_file->link) {
377 5 : entry = phar_get_link_source(data->internal_file TSRMLS_CC);
378 : } else {
379 969 : entry = data->internal_file;
380 : }
381 :
382 974 : if (entry->is_deleted) {
383 0 : stream->eof = 1;
384 0 : return 0;
385 : }
386 :
387 : /* use our proxy position */
388 974 : php_stream_seek(data->fp, data->position + data->zero, SEEK_SET);
389 :
390 974 : got = php_stream_read(data->fp, buf, MIN(count, entry->uncompressed_filesize - data->position));
391 974 : data->position = php_stream_tell(data->fp) - data->zero;
392 974 : stream->eof = (data->position == (off_t) entry->uncompressed_filesize);
393 :
394 974 : return got;
395 : }
396 : /* }}} */
397 :
398 : /**
399 : * Used for fseek($fp) on a phar file handle
400 : */
401 : static int phar_stream_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
402 42 : {
403 42 : phar_entry_data *data = (phar_entry_data *)stream->abstract;
404 : phar_entry_info *entry;
405 : int res;
406 : off_t temp;
407 :
408 42 : if (data->internal_file->link) {
409 1 : entry = phar_get_link_source(data->internal_file TSRMLS_CC);
410 : } else {
411 41 : entry = data->internal_file;
412 : }
413 :
414 42 : switch (whence) {
415 : case SEEK_END :
416 6 : temp = data->zero + entry->uncompressed_filesize + offset;
417 6 : break;
418 : case SEEK_CUR :
419 0 : temp = data->zero + data->position + offset;
420 0 : break;
421 : case SEEK_SET :
422 36 : temp = data->zero + offset;
423 : break;
424 : }
425 42 : if (temp > data->zero + (off_t) entry->uncompressed_filesize) {
426 4 : *newoffset = -1;
427 4 : return -1;
428 : }
429 38 : if (temp < data->zero) {
430 4 : *newoffset = -1;
431 4 : return -1;
432 : }
433 34 : res = php_stream_seek(data->fp, temp, SEEK_SET);
434 34 : *newoffset = php_stream_tell(data->fp) - data->zero;
435 34 : data->position = *newoffset;
436 34 : return res;
437 : }
438 : /* }}} */
439 :
440 : /**
441 : * Used for writing to a phar file
442 : */
443 : static size_t phar_stream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
444 45 : {
445 45 : phar_entry_data *data = (phar_entry_data *) stream->abstract;
446 :
447 45 : php_stream_seek(data->fp, data->position, SEEK_SET);
448 45 : if (count != php_stream_write(data->fp, buf, count)) {
449 0 : php_stream_wrapper_log_error(stream->wrapper, stream->flags TSRMLS_CC, "phar error: Could not write %d characters to \"%s\" in phar \"%s\"", (int) count, data->internal_file->filename, data->phar->fname);
450 0 : return -1;
451 : }
452 45 : data->position = php_stream_tell(data->fp);
453 45 : if (data->position > (off_t)data->internal_file->uncompressed_filesize) {
454 45 : data->internal_file->uncompressed_filesize = data->position;
455 : }
456 45 : data->internal_file->compressed_filesize = data->internal_file->uncompressed_filesize;
457 45 : data->internal_file->old_flags = data->internal_file->flags;
458 45 : data->internal_file->is_modified = 1;
459 45 : return count;
460 : }
461 : /* }}} */
462 :
463 : /**
464 : * Used to save work done on a writeable phar
465 : */
466 : static int phar_stream_flush(php_stream *stream TSRMLS_DC) /* {{{ */
467 552 : {
468 : char *error;
469 : int ret;
470 552 : if (stream->mode[0] == 'w' || (stream->mode[0] == 'r' && stream->mode[1] == '+')) {
471 47 : ret = phar_flush(((phar_entry_data *)stream->abstract)->phar, 0, 0, 0, &error TSRMLS_CC);
472 47 : if (error) {
473 0 : php_stream_wrapper_log_error(stream->wrapper, REPORT_ERRORS TSRMLS_CC, error);
474 0 : efree(error);
475 : }
476 47 : return ret;
477 : } else {
478 505 : return EOF;
479 : }
480 : }
481 : /* }}} */
482 :
483 : /* {{{ phar_dostat */
484 : /**
485 : * stat an opened phar file handle stream, used by phar_stat()
486 : */
487 : void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_temp_dir TSRMLS_DC)
488 1090 : {
489 1090 : memset(ssb, 0, sizeof(php_stream_statbuf));
490 :
491 2045 : if (!is_temp_dir && !data->is_dir) {
492 955 : ssb->sb.st_size = data->uncompressed_filesize;
493 955 : ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
494 955 : ssb->sb.st_mode |= S_IFREG; /* regular file */
495 : /* timestamp is just the timestamp when this was added to the phar */
496 : #ifdef NETWARE
497 : ssb->sb.st_mtime.tv_sec = data->timestamp;
498 : ssb->sb.st_atime.tv_sec = data->timestamp;
499 : ssb->sb.st_ctime.tv_sec = data->timestamp;
500 : #else
501 955 : ssb->sb.st_mtime = data->timestamp;
502 955 : ssb->sb.st_atime = data->timestamp;
503 955 : ssb->sb.st_ctime = data->timestamp;
504 : #endif
505 206 : } else if (!is_temp_dir && data->is_dir) {
506 71 : ssb->sb.st_size = 0;
507 71 : ssb->sb.st_mode = data->flags & PHAR_ENT_PERM_MASK;
508 71 : ssb->sb.st_mode |= S_IFDIR; /* regular directory */
509 : /* timestamp is just the timestamp when this was added to the phar */
510 : #ifdef NETWARE
511 : ssb->sb.st_mtime.tv_sec = data->timestamp;
512 : ssb->sb.st_atime.tv_sec = data->timestamp;
513 : ssb->sb.st_ctime.tv_sec = data->timestamp;
514 : #else
515 71 : ssb->sb.st_mtime = data->timestamp;
516 71 : ssb->sb.st_atime = data->timestamp;
517 71 : ssb->sb.st_ctime = data->timestamp;
518 : #endif
519 : } else {
520 64 : ssb->sb.st_size = 0;
521 64 : ssb->sb.st_mode = 0777;
522 64 : ssb->sb.st_mode |= S_IFDIR; /* regular directory */
523 : #ifdef NETWARE
524 : ssb->sb.st_mtime.tv_sec = phar->max_timestamp;
525 : ssb->sb.st_atime.tv_sec = phar->max_timestamp;
526 : ssb->sb.st_ctime.tv_sec = phar->max_timestamp;
527 : #else
528 64 : ssb->sb.st_mtime = phar->max_timestamp;
529 64 : ssb->sb.st_atime = phar->max_timestamp;
530 64 : ssb->sb.st_ctime = phar->max_timestamp;
531 : #endif
532 : }
533 1090 : if (!phar->is_writeable) {
534 527 : ssb->sb.st_mode = (ssb->sb.st_mode & 0555) | (ssb->sb.st_mode & ~0777);
535 : }
536 :
537 1090 : ssb->sb.st_nlink = 1;
538 1090 : ssb->sb.st_rdev = -1;
539 : /* this is only for APC, so use /dev/null device - no chance of conflict there! */
540 1090 : ssb->sb.st_dev = 0xc;
541 : /* generate unique inode number for alias/filename, so no phars will conflict */
542 1090 : if (!is_temp_dir) {
543 1026 : ssb->sb.st_ino = data->inode;
544 : }
545 : #ifndef PHP_WIN32
546 1090 : ssb->sb.st_blksize = -1;
547 1090 : ssb->sb.st_blocks = -1;
548 : #endif
549 1090 : }
550 : /* }}}*/
551 :
552 : /**
553 : * Stat an opened phar file handle
554 : */
555 : static int phar_stream_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) /* {{{ */
556 737 : {
557 737 : phar_entry_data *data = (phar_entry_data *)stream->abstract;
558 :
559 : /* If ssb is NULL then someone is misbehaving */
560 737 : if (!ssb) {
561 0 : return -1;
562 : }
563 :
564 737 : phar_dostat(data->phar, data->internal_file, ssb, 0 TSRMLS_CC);
565 737 : return 0;
566 : }
567 : /* }}} */
568 :
569 : /**
570 : * Stream wrapper stat implementation of stat()
571 : */
572 : static int phar_wrapper_stat(php_stream_wrapper *wrapper, char *url, int flags,
573 : php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC) /* {{{ */
574 391 : {
575 391 : php_url *resource = NULL;
576 : char *internal_file, *error;
577 : phar_archive_data *phar;
578 : phar_entry_info *entry;
579 : uint host_len;
580 : int internal_file_len;
581 :
582 391 : if ((resource = phar_parse_url(wrapper, url, "r", flags|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
583 6 : return FAILURE;
584 : }
585 :
586 : /* we must have at the very least phar://alias.phar/internalfile.php */
587 385 : if (!resource->scheme || !resource->host || !resource->path) {
588 0 : php_url_free(resource);
589 0 : return FAILURE;
590 : }
591 :
592 385 : if (strcasecmp("phar", resource->scheme)) {
593 0 : php_url_free(resource);
594 0 : return FAILURE;
595 : }
596 :
597 385 : host_len = strlen(resource->host);
598 385 : phar_request_initialize(TSRMLS_C);
599 :
600 385 : internal_file = resource->path + 1; /* strip leading "/" */
601 : /* find the phar in our trusty global hash indexed by alias (host of phar://blah.phar/file.whatever) */
602 385 : if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
603 0 : php_url_free(resource);
604 0 : if (error) {
605 0 : efree(error);
606 : }
607 0 : return FAILURE;
608 : }
609 385 : if (error) {
610 0 : efree(error);
611 : }
612 385 : if (*internal_file == '\0') {
613 : /* root directory requested */
614 1 : phar_dostat(phar, NULL, ssb, 1 TSRMLS_CC);
615 1 : php_url_free(resource);
616 1 : return SUCCESS;
617 : }
618 384 : if (!phar->manifest.arBuckets) {
619 0 : php_url_free(resource);
620 0 : return FAILURE;
621 : }
622 384 : internal_file_len = strlen(internal_file);
623 : /* search through the manifest of files, and if we have an exact match, it's a file */
624 384 : if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) {
625 287 : phar_dostat(phar, entry, ssb, 0 TSRMLS_CC);
626 287 : php_url_free(resource);
627 287 : return SUCCESS;
628 : }
629 97 : if (zend_hash_exists(&(phar->virtual_dirs), internal_file, internal_file_len)) {
630 63 : phar_dostat(phar, NULL, ssb, 1 TSRMLS_CC);
631 63 : php_url_free(resource);
632 63 : return SUCCESS;
633 : }
634 : /* check for mounted directories */
635 34 : if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) {
636 : phar_zstr key;
637 : char *str_key;
638 : ulong unused;
639 : uint keylen;
640 : HashPosition pos;
641 :
642 8 : zend_hash_internal_pointer_reset_ex(&phar->mounted_dirs, &pos);
643 8 : while (FAILURE != zend_hash_has_more_elements_ex(&phar->mounted_dirs, &pos)) {
644 8 : if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &keylen, &unused, 0, &pos)) {
645 0 : break;
646 : }
647 8 : PHAR_STR(key, str_key);
648 8 : if ((int)keylen >= internal_file_len || strncmp(str_key, internal_file, keylen)) {
649 6 : zend_hash_move_forward_ex(&phar->mounted_dirs, &pos);
650 : PHAR_STR_FREE(str_key);
651 6 : continue;
652 : } else {
653 : char *test;
654 : int test_len;
655 : php_stream_statbuf ssbi;
656 :
657 2 : if (SUCCESS != zend_hash_find(&phar->manifest, str_key, keylen, (void **) &entry)) {
658 : PHAR_STR_FREE(str_key);
659 0 : goto free_resource;
660 : }
661 : PHAR_STR_FREE(str_key);
662 2 : if (!entry->tmp || !entry->is_mounted) {
663 : goto free_resource;
664 : }
665 2 : test_len = spprintf(&test, MAXPATHLEN, "%s%s", entry->tmp, internal_file + keylen);
666 2 : if (SUCCESS != php_stream_stat_path(test, &ssbi)) {
667 0 : efree(test);
668 0 : zend_hash_move_forward_ex(&phar->mounted_dirs, &pos);
669 0 : continue;
670 : }
671 : /* mount the file/directory just in time */
672 2 : if (SUCCESS != phar_mount_entry(phar, test, test_len, internal_file, internal_file_len TSRMLS_CC)) {
673 0 : efree(test);
674 0 : goto free_resource;
675 : }
676 2 : efree(test);
677 2 : if (SUCCESS != zend_hash_find(&phar->manifest, internal_file, internal_file_len, (void**)&entry)) {
678 0 : goto free_resource;
679 : }
680 2 : phar_dostat(phar, entry, ssb, 0 TSRMLS_CC);
681 2 : php_url_free(resource);
682 2 : return SUCCESS;
683 : }
684 : }
685 : }
686 32 : free_resource:
687 32 : php_url_free(resource);
688 32 : return FAILURE;
689 : }
690 : /* }}} */
691 :
692 : /**
693 : * Unlink a file within a phar archive
694 : */
695 : static int phar_wrapper_unlink(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
696 19 : {
697 : php_url *resource;
698 : char *internal_file, *error;
699 : int internal_file_len;
700 : phar_entry_data *idata;
701 : phar_archive_data **pphar;
702 : uint host_len;
703 :
704 19 : if ((resource = phar_parse_url(wrapper, url, "rb", options TSRMLS_CC)) == NULL) {
705 3 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: unlink failed");
706 3 : return 0;
707 : }
708 :
709 : /* we must have at the very least phar://alias.phar/internalfile.php */
710 16 : if (!resource->scheme || !resource->host || !resource->path) {
711 0 : php_url_free(resource);
712 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
713 0 : return 0;
714 : }
715 :
716 16 : if (strcasecmp("phar", resource->scheme)) {
717 0 : php_url_free(resource);
718 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
719 0 : return 0;
720 : }
721 :
722 16 : host_len = strlen(resource->host);
723 16 : phar_request_initialize(TSRMLS_C);
724 :
725 16 : if (FAILURE == zend_hash_find(&(PHAR_GLOBALS->phar_fname_map), resource->host, host_len, (void **) &pphar)) {
726 0 : pphar = NULL;
727 : }
728 16 : if (PHAR_G(readonly) && (!pphar || !(*pphar)->is_data)) {
729 3 : php_url_free(resource);
730 3 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: write operations disabled by INI setting");
731 3 : return 0;
732 : }
733 :
734 : /* need to copy to strip leading "/", will get touched again */
735 13 : internal_file = estrdup(resource->path + 1);
736 13 : internal_file_len = strlen(internal_file);
737 13 : if (FAILURE == phar_get_entry_data(&idata, resource->host, host_len, internal_file, internal_file_len, "r", 0, &error, 1 TSRMLS_CC)) {
738 : /* constraints of fp refcount were not met */
739 1 : if (error) {
740 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "unlink of \"%s\" failed: %s", url, error);
741 0 : efree(error);
742 : } else {
743 1 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "unlink of \"%s\" failed, file does not exist", url);
744 : }
745 1 : efree(internal_file);
746 1 : php_url_free(resource);
747 1 : return 0;
748 : }
749 12 : if (error) {
750 0 : efree(error);
751 : }
752 12 : if (idata->internal_file->fp_refcount > 1) {
753 : /* more than just our fp resource is open for this file */
754 3 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: \"%s\" in phar \"%s\", has open file pointers, cannot unlink", internal_file, resource->host);
755 3 : efree(internal_file);
756 3 : php_url_free(resource);
757 3 : phar_entry_delref(idata TSRMLS_CC);
758 3 : return 0;
759 : }
760 9 : php_url_free(resource);
761 9 : efree(internal_file);
762 9 : phar_entry_remove(idata, &error TSRMLS_CC);
763 9 : if (error) {
764 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
765 0 : efree(error);
766 : }
767 9 : return 1;
768 : }
769 : /* }}} */
770 :
771 : static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char *url_to, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
772 15 : {
773 : php_url *resource_from, *resource_to;
774 : char *error;
775 : phar_archive_data *phar, *pfrom, *pto;
776 : phar_entry_info *entry;
777 : uint host_len;
778 15 : int is_dir = 0;
779 15 : int is_modified = 0;
780 :
781 15 : error = NULL;
782 :
783 15 : if ((resource_from = phar_parse_url(wrapper, url_from, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
784 2 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_from);
785 2 : return 0;
786 : }
787 13 : if (SUCCESS != phar_get_archive(&pfrom, resource_from->host, strlen(resource_from->host), NULL, 0, &error TSRMLS_CC)) {
788 0 : pfrom = NULL;
789 0 : if (error) {
790 0 : efree(error);
791 : }
792 : }
793 13 : if (PHAR_G(readonly) && (!pfrom || !pfrom->is_data)) {
794 0 : php_url_free(resource_from);
795 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: write operations disabled by phar.readonly INI setting");
796 0 : return 0;
797 : }
798 :
799 13 : if ((resource_to = phar_parse_url(wrapper, url_to, "wb", options|PHP_STREAM_URL_STAT_QUIET TSRMLS_CC)) == NULL) {
800 2 : php_url_free(resource_from);
801 2 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid or non-writable url \"%s\"", url_from, url_to, url_to);
802 2 : return 0;
803 : }
804 11 : if (SUCCESS != phar_get_archive(&pto, resource_to->host, strlen(resource_to->host), NULL, 0, &error TSRMLS_CC)) {
805 0 : if (error) {
806 0 : efree(error);
807 : }
808 0 : pto = NULL;
809 : }
810 11 : if (PHAR_G(readonly) && (!pto || !pto->is_data)) {
811 0 : php_url_free(resource_from);
812 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: write operations disabled by phar.readonly INI setting");
813 0 : return 0;
814 : }
815 :
816 11 : if (strcmp(resource_from->host, resource_to->host)) {
817 1 : php_url_free(resource_from);
818 1 : php_url_free(resource_to);
819 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\", not within the same phar archive", url_from, url_to);
820 1 : return 0;
821 : }
822 :
823 : /* we must have at the very least phar://alias.phar/internalfile.php */
824 10 : if (!resource_from->scheme || !resource_from->host || !resource_from->path) {
825 0 : php_url_free(resource_from);
826 0 : php_url_free(resource_to);
827 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_from);
828 0 : return 0;
829 : }
830 :
831 10 : if (!resource_to->scheme || !resource_to->host || !resource_to->path) {
832 0 : php_url_free(resource_from);
833 0 : php_url_free(resource_to);
834 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": invalid url \"%s\"", url_from, url_to, url_to);
835 0 : return 0;
836 : }
837 :
838 10 : if (strcasecmp("phar", resource_from->scheme)) {
839 0 : php_url_free(resource_from);
840 0 : php_url_free(resource_to);
841 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_from);
842 0 : return 0;
843 : }
844 :
845 10 : if (strcasecmp("phar", resource_to->scheme)) {
846 0 : php_url_free(resource_from);
847 0 : php_url_free(resource_to);
848 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": not a phar stream url \"%s\"", url_from, url_to, url_to);
849 0 : return 0;
850 : }
851 :
852 10 : host_len = strlen(resource_from->host);
853 :
854 10 : if (SUCCESS != phar_get_archive(&phar, resource_from->host, host_len, NULL, 0, &error TSRMLS_CC)) {
855 0 : php_url_free(resource_from);
856 0 : php_url_free(resource_to);
857 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
858 0 : efree(error);
859 0 : return 0;
860 : }
861 :
862 10 : if (phar->is_persistent && FAILURE == phar_copy_on_write(&phar TSRMLS_CC)) {
863 0 : php_url_free(resource_from);
864 0 : php_url_free(resource_to);
865 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": could not make cached phar writeable", url_from, url_to);
866 0 : return 0;
867 : }
868 :
869 10 : if (SUCCESS == zend_hash_find(&(phar->manifest), resource_from->path+1, strlen(resource_from->path)-1, (void **)&entry)) {
870 : phar_entry_info new, *source;
871 :
872 : /* perform rename magic */
873 5 : if (entry->is_deleted) {
874 0 : php_url_free(resource_from);
875 0 : php_url_free(resource_to);
876 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source has been deleted", url_from, url_to);
877 0 : return 0;
878 : }
879 : /* transfer all data over to the new entry */
880 5 : memcpy((void *) &new, (void *) entry, sizeof(phar_entry_info));
881 : /* mark the old one for deletion */
882 5 : entry->is_deleted = 1;
883 5 : entry->fp = NULL;
884 5 : entry->metadata = 0;
885 5 : entry->link = entry->tmp = NULL;
886 5 : source = entry;
887 :
888 : /* add to the manifest, and then store the pointer to the new guy in entry */
889 5 : zend_hash_add(&(phar->manifest), resource_to->path+1, strlen(resource_to->path)-1, (void **)&new, sizeof(phar_entry_info), (void **) &entry);
890 :
891 5 : entry->filename = estrdup(resource_to->path+1);
892 5 : if (FAILURE == phar_copy_entry_fp(source, entry, &error TSRMLS_CC)) {
893 0 : php_url_free(resource_from);
894 0 : php_url_free(resource_to);
895 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
896 0 : efree(error);
897 0 : zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
898 0 : return 0;
899 : }
900 5 : is_modified = 1;
901 5 : entry->is_modified = 1;
902 5 : entry->filename_len = strlen(entry->filename);
903 5 : is_dir = entry->is_dir;
904 : } else {
905 5 : is_dir = zend_hash_exists(&(phar->virtual_dirs), resource_from->path+1, strlen(resource_from->path)-1);
906 5 : if (!is_dir) {
907 : /* file does not exist */
908 1 : php_url_free(resource_from);
909 1 : php_url_free(resource_to);
910 1 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\" from extracted phar archive, source does not exist", url_from, url_to);
911 1 : return 0;
912 :
913 : }
914 : }
915 :
916 : /* Rename directory. Update all nested paths */
917 9 : if (is_dir) {
918 : int key_type;
919 : phar_zstr key, new_key;
920 : char *str_key, *new_str_key;
921 : uint key_len, new_key_len;
922 : ulong unused;
923 4 : uint from_len = strlen(resource_from->path+1);
924 4 : uint to_len = strlen(resource_to->path+1);
925 :
926 4 : for (zend_hash_internal_pointer_reset(&phar->manifest);
927 17 : HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL)) &&
928 : SUCCESS == zend_hash_get_current_data(&phar->manifest, (void **) &entry);
929 9 : zend_hash_move_forward(&phar->manifest)) {
930 :
931 9 : PHAR_STR(key, str_key);
932 :
933 9 : if (!entry->is_deleted &&
934 : key_len > from_len &&
935 : memcmp(str_key, resource_from->path+1, from_len) == 0 &&
936 : IS_SLASH(str_key[from_len])) {
937 :
938 7 : new_key_len = key_len + to_len - from_len;
939 7 : new_str_key = emalloc(new_key_len+1);
940 7 : memcpy(new_str_key, resource_to->path + 1, to_len);
941 7 : memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
942 7 : new_str_key[new_key_len] = 0;
943 :
944 7 : is_modified = 1;
945 7 : entry->is_modified = 1;
946 7 : efree(entry->filename);
947 7 : entry->filename = new_str_key;
948 7 : entry->filename_len = new_key_len;
949 :
950 7 : PHAR_ZSTR(new_str_key, new_key);
951 : #if PHP_VERSION_ID < 50300
952 : zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, NULL);
953 : #else
954 7 : zend_hash_update_current_key_ex(&phar->manifest, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
955 : #endif
956 : }
957 : PHAR_STR_FREE(str_key);
958 : }
959 :
960 4 : for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
961 15 : HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL));
962 7 : zend_hash_move_forward(&phar->virtual_dirs)) {
963 :
964 7 : PHAR_STR(key, str_key);
965 :
966 7 : if (key_len >= from_len &&
967 : memcmp(str_key, resource_from->path+1, from_len) == 0 &&
968 : (key_len == from_len || IS_SLASH(str_key[from_len]))) {
969 :
970 5 : new_key_len = key_len + to_len - from_len;
971 5 : new_str_key = emalloc(new_key_len+1);
972 5 : memcpy(new_str_key, resource_to->path + 1, to_len);
973 5 : memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
974 5 : new_str_key[new_key_len] = 0;
975 :
976 5 : PHAR_ZSTR(new_str_key, new_key);
977 : #if PHP_VERSION_ID < 50300
978 : zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, NULL);
979 : #else
980 5 : zend_hash_update_current_key_ex(&phar->virtual_dirs, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
981 : #endif
982 5 : efree(new_str_key);
983 : }
984 : PHAR_STR_FREE(str_key);
985 : }
986 :
987 4 : for (zend_hash_internal_pointer_reset(&phar->mounted_dirs);
988 9 : HASH_KEY_NON_EXISTANT != (key_type = zend_hash_get_current_key_ex(&phar->mounted_dirs, &key, &key_len, &unused, 0, NULL)) &&
989 : SUCCESS == zend_hash_get_current_data(&phar->mounted_dirs, (void **) &entry);
990 1 : zend_hash_move_forward(&phar->mounted_dirs)) {
991 :
992 1 : PHAR_STR(key, str_key);
993 :
994 1 : if (key_len >= from_len &&
995 : memcmp(str_key, resource_from->path+1, from_len) == 0 &&
996 : (key_len == from_len || IS_SLASH(str_key[from_len]))) {
997 :
998 1 : new_key_len = key_len + to_len - from_len;
999 1 : new_str_key = emalloc(new_key_len+1);
1000 1 : memcpy(new_str_key, resource_to->path + 1, to_len);
1001 1 : memcpy(new_str_key + to_len, str_key + from_len, key_len - from_len);
1002 1 : new_str_key[new_key_len] = 0;
1003 :
1004 1 : PHAR_ZSTR(new_str_key, new_key);
1005 : #if PHP_VERSION_ID < 50300
1006 : zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, NULL);
1007 : #else
1008 1 : zend_hash_update_current_key_ex(&phar->mounted_dirs, key_type, new_key, new_key_len, 0, HASH_UPDATE_KEY_ANYWAY, NULL);
1009 : #endif
1010 1 : efree(new_str_key);
1011 : }
1012 : PHAR_STR_FREE(str_key);
1013 : }
1014 : }
1015 :
1016 9 : if (is_modified) {
1017 9 : phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
1018 9 : if (error) {
1019 0 : php_url_free(resource_from);
1020 0 : php_url_free(resource_to);
1021 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "phar error: cannot rename \"%s\" to \"%s\": %s", url_from, url_to, error);
1022 0 : efree(error);
1023 0 : return 0;
1024 : }
1025 : }
1026 :
1027 9 : php_url_free(resource_from);
1028 9 : php_url_free(resource_to);
1029 :
1030 9 : return 1;
1031 : }
1032 : /* }}} */
1033 :
1034 : /*
1035 : * Local variables:
1036 : * tab-width: 4
1037 : * c-basic-offset: 4
1038 : * End:
1039 : * vim600: noet sw=4 ts=4 fdm=marker
1040 : * vim<600: noet sw=4 ts=4
1041 : */
|