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