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_DIRSTREAM 1
21 : #include "phar_internal.h"
22 : #include "dirstream.h"
23 :
24 : BEGIN_EXTERN_C()
25 : void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_dir TSRMLS_DC);
26 : END_EXTERN_C()
27 :
28 : php_stream_ops phar_dir_ops = {
29 : phar_dir_write, /* write */
30 : phar_dir_read, /* read */
31 : phar_dir_close, /* close */
32 : phar_dir_flush, /* flush */
33 : "phar dir",
34 : phar_dir_seek, /* seek */
35 : NULL, /* cast */
36 : NULL, /* stat */
37 : NULL, /* set option */
38 : };
39 :
40 : /**
41 : * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle
42 : */
43 : static int phar_dir_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
44 449 : {
45 449 : HashTable *data = (HashTable *)stream->abstract;
46 :
47 449 : if (data && data->arBuckets) {
48 449 : zend_hash_destroy(data);
49 449 : data->arBuckets = 0;
50 449 : FREE_HASHTABLE(data);
51 449 : stream->abstract = NULL;
52 : }
53 :
54 449 : return 0;
55 : }
56 : /* }}} */
57 :
58 : /**
59 : * Used for seeking on a phar directory handle
60 : */
61 : static int phar_dir_seek(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC) /* {{{ */
62 64 : {
63 64 : HashTable *data = (HashTable *)stream->abstract;
64 :
65 64 : if (!data) {
66 0 : return -1;
67 : }
68 :
69 64 : if (whence == SEEK_END) {
70 1 : whence = SEEK_SET;
71 1 : offset = zend_hash_num_elements(data) + offset;
72 : }
73 :
74 64 : if (whence == SEEK_SET) {
75 64 : zend_hash_internal_pointer_reset(data);
76 : }
77 :
78 64 : if (offset < 0) {
79 1 : return -1;
80 : } else {
81 63 : *newoffset = 0;
82 131 : while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) {
83 5 : ++(*newoffset);
84 : }
85 63 : return 0;
86 : }
87 : }
88 : /* }}} */
89 :
90 : /**
91 : * Used for readdir() on an opendir()ed phar directory handle
92 : */
93 : static size_t phar_dir_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
94 682 : {
95 : size_t to_read;
96 682 : HashTable *data = (HashTable *)stream->abstract;
97 : phar_zstr key;
98 : char *str_key;
99 : uint keylen;
100 : ulong unused;
101 :
102 682 : if (FAILURE == zend_hash_has_more_elements(data)) {
103 252 : return 0;
104 : }
105 :
106 430 : if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(data, &key, &keylen, &unused, 0, NULL)) {
107 0 : return 0;
108 : }
109 :
110 430 : PHAR_STR(key, str_key);
111 430 : zend_hash_move_forward(data);
112 430 : to_read = MIN(keylen, count);
113 :
114 430 : if (to_read == 0 || count < keylen) {
115 : PHAR_STR_FREE(str_key);
116 0 : return 0;
117 : }
118 :
119 430 : memset(buf, 0, sizeof(php_stream_dirent));
120 430 : memcpy(((php_stream_dirent *) buf)->d_name, str_key, to_read);
121 : PHAR_STR_FREE(str_key);
122 430 : ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0';
123 :
124 430 : return sizeof(php_stream_dirent);
125 : }
126 : /* }}} */
127 :
128 : /**
129 : * Dummy: Used for writing to a phar directory (i.e. not used)
130 : */
131 : static size_t phar_dir_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
132 1 : {
133 1 : return 0;
134 : }
135 : /* }}} */
136 :
137 : /**
138 : * Dummy: Used for flushing writes to a phar directory (i.e. not used)
139 : */
140 : static int phar_dir_flush(php_stream *stream TSRMLS_DC) /* {{{ */
141 449 : {
142 449 : return EOF;
143 : }
144 : /* }}} */
145 :
146 : /**
147 : * add an empty element with a char * key to a hash table, avoiding duplicates
148 : *
149 : * This is used to get a unique listing of virtual directories within a phar,
150 : * for iterating over opendir()ed phar directories.
151 : */
152 : static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength) /* {{{ */
153 910 : {
154 910 : void *dummy = (char *) 1;
155 :
156 910 : return zend_hash_update(ht, arKey, nKeyLength, (void *) &dummy, sizeof(void *), NULL);
157 : }
158 : /* }}} */
159 :
160 : /**
161 : * Used for sorting directories alphabetically
162 : */
163 : static int phar_compare_dir_name(const void *a, const void *b TSRMLS_DC) /* {{{ */
164 541 : {
165 : Bucket *f;
166 : Bucket *s;
167 : int result;
168 :
169 541 : f = *((Bucket **) a);
170 541 : s = *((Bucket **) b);
171 : #if (PHP_MAJOR_VERSION < 6)
172 541 : result = zend_binary_strcmp(f->arKey, f->nKeyLength, s->arKey, s->nKeyLength);
173 : #else
174 : result = zend_binary_strcmp(f->key.arKey.s, f->nKeyLength, s->key.arKey.s, s->nKeyLength);
175 : #endif
176 :
177 541 : if (result < 0) {
178 122 : return -1;
179 419 : } else if (result > 0) {
180 419 : return 1;
181 : } else {
182 0 : return 0;
183 : }
184 : }
185 : /* }}} */
186 :
187 : /**
188 : * Create a opendir() directory stream handle by iterating over each of the
189 : * files in a phar and retrieving its relative path. From this, construct
190 : * a list of files/directories that are "in" the directory represented by dir
191 : */
192 : static php_stream *phar_make_dirstream(char *dir, HashTable *manifest TSRMLS_DC) /* {{{ */
193 449 : {
194 : HashTable *data;
195 449 : int dirlen = strlen(dir);
196 : phar_zstr key;
197 : char *entry, *found, *save, *str_key;
198 : uint keylen;
199 : ulong unused;
200 :
201 449 : ALLOC_HASHTABLE(data);
202 449 : zend_hash_init(data, 64, zend_get_hash_value, NULL, 0);
203 :
204 449 : if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || (dirlen >= sizeof(".phar")-1 && !memcmp(dir, ".phar", sizeof(".phar")-1))) {
205 : /* make empty root directory for empty phar */
206 : /* make empty directory for .phar magic directory */
207 159 : efree(dir);
208 159 : return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
209 : }
210 :
211 290 : zend_hash_internal_pointer_reset(manifest);
212 :
213 2333 : while (FAILURE != zend_hash_has_more_elements(manifest)) {
214 1753 : if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
215 0 : break;
216 : }
217 :
218 1753 : PHAR_STR(key, str_key);
219 :
220 1753 : if (keylen <= (uint)dirlen) {
221 633 : if (keylen < (uint)dirlen || !strncmp(str_key, dir, dirlen)) {
222 : PHAR_STR_FREE(str_key);
223 290 : if (SUCCESS != zend_hash_move_forward(manifest)) {
224 0 : break;
225 : }
226 290 : continue;
227 : }
228 : }
229 :
230 1463 : if (*dir == '/') {
231 : /* root directory */
232 841 : if (keylen >= sizeof(".phar")-1 && !memcmp(str_key, ".phar", sizeof(".phar")-1)) {
233 : PHAR_STR_FREE(str_key);
234 : /* do not add any magic entries to this directory */
235 102 : if (SUCCESS != zend_hash_move_forward(manifest)) {
236 0 : break;
237 : }
238 102 : continue;
239 : }
240 :
241 739 : if (NULL != (found = (char *) memchr(str_key, '/', keylen))) {
242 : /* the entry has a path separator and is a subdirectory */
243 210 : entry = (char *) safe_emalloc(found - str_key, 1, 1);
244 210 : memcpy(entry, str_key, found - str_key);
245 210 : keylen = found - str_key;
246 210 : entry[keylen] = '\0';
247 : } else {
248 529 : entry = (char *) safe_emalloc(keylen, 1, 1);
249 529 : memcpy(entry, str_key, keylen);
250 529 : entry[keylen] = '\0';
251 : }
252 :
253 : PHAR_STR_FREE(str_key);
254 739 : goto PHAR_ADD_ENTRY;
255 : } else {
256 622 : if (0 != memcmp(str_key, dir, dirlen)) {
257 : /* entry in directory not found */
258 : PHAR_STR_FREE(str_key);
259 444 : if (SUCCESS != zend_hash_move_forward(manifest)) {
260 0 : break;
261 : }
262 444 : continue;
263 : } else {
264 178 : if (str_key[dirlen] != '/') {
265 : PHAR_STR_FREE(str_key);
266 7 : if (SUCCESS != zend_hash_move_forward(manifest)) {
267 0 : break;
268 : }
269 7 : continue;
270 : }
271 : }
272 : }
273 :
274 171 : save = str_key;
275 171 : save += dirlen + 1; /* seek to just past the path separator */
276 :
277 171 : if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) {
278 : /* is subdirectory */
279 97 : save -= dirlen + 1;
280 97 : entry = (char *) safe_emalloc(found - save + dirlen, 1, 1);
281 97 : memcpy(entry, save + dirlen + 1, found - save - dirlen - 1);
282 97 : keylen = found - save - dirlen - 1;
283 97 : entry[keylen] = '\0';
284 : } else {
285 : /* is file */
286 74 : save -= dirlen + 1;
287 74 : entry = (char *) safe_emalloc(keylen - dirlen, 1, 1);
288 74 : memcpy(entry, save + dirlen + 1, keylen - dirlen - 1);
289 74 : entry[keylen - dirlen - 1] = '\0';
290 74 : keylen = keylen - dirlen - 1;
291 : }
292 : PHAR_STR_FREE(str_key);
293 910 : PHAR_ADD_ENTRY:
294 910 : if (keylen) {
295 910 : phar_add_empty(data, entry, keylen);
296 : }
297 :
298 910 : efree(entry);
299 :
300 910 : if (SUCCESS != zend_hash_move_forward(manifest)) {
301 0 : break;
302 : }
303 : }
304 :
305 290 : if (FAILURE != zend_hash_has_more_elements(data)) {
306 275 : efree(dir);
307 275 : if (zend_hash_sort(data, zend_qsort, phar_compare_dir_name, 0 TSRMLS_CC) == FAILURE) {
308 0 : FREE_HASHTABLE(data);
309 0 : return NULL;
310 : }
311 275 : return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
312 : } else {
313 15 : efree(dir);
314 15 : return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
315 : }
316 : }
317 : /* }}}*/
318 :
319 : /**
320 : * Open a directory handle within a phar archive
321 : */
322 : php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC) /* {{{ */
323 458 : {
324 458 : php_url *resource = NULL;
325 : php_stream *ret;
326 : char *internal_file, *error, *str_key;
327 : phar_zstr key;
328 : uint keylen;
329 : ulong unused;
330 : phar_archive_data *phar;
331 458 : phar_entry_info *entry = NULL;
332 : uint host_len;
333 :
334 458 : if ((resource = phar_parse_url(wrapper, path, mode, options TSRMLS_CC)) == NULL) {
335 6 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar url \"%s\" is unknown", path);
336 6 : return NULL;
337 : }
338 :
339 : /* we must have at the very least phar://alias.phar/ */
340 452 : if (!resource->scheme || !resource->host || !resource->path) {
341 0 : if (resource->host && !resource->path) {
342 0 : 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)", path, resource->host);
343 0 : php_url_free(resource);
344 0 : return NULL;
345 : }
346 0 : php_url_free(resource);
347 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
348 0 : return NULL;
349 : }
350 :
351 452 : if (strcasecmp("phar", resource->scheme)) {
352 0 : php_url_free(resource);
353 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar url \"%s\"", path);
354 0 : return NULL;
355 : }
356 :
357 452 : host_len = strlen(resource->host);
358 452 : phar_request_initialize(TSRMLS_C);
359 452 : internal_file = resource->path + 1; /* strip leading "/" */
360 :
361 452 : if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
362 0 : if (error) {
363 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, error);
364 0 : efree(error);
365 : } else {
366 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar file \"%s\" is unknown", resource->host);
367 : }
368 0 : php_url_free(resource);
369 0 : return NULL;
370 : }
371 :
372 452 : if (error) {
373 0 : efree(error);
374 : }
375 :
376 452 : if (*internal_file == '\0') {
377 : /* root directory requested */
378 397 : internal_file = estrndup(internal_file - 1, 1);
379 397 : ret = phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
380 397 : php_url_free(resource);
381 397 : return ret;
382 : }
383 :
384 55 : if (!phar->manifest.arBuckets) {
385 0 : php_url_free(resource);
386 0 : return NULL;
387 : }
388 :
389 55 : if (SUCCESS == zend_hash_find(&phar->manifest, internal_file, strlen(internal_file), (void**)&entry) && !entry->is_dir) {
390 0 : php_url_free(resource);
391 0 : return NULL;
392 55 : } else if (entry && entry->is_dir) {
393 24 : if (entry->is_mounted) {
394 2 : php_url_free(resource);
395 2 : return php_stream_opendir(entry->tmp, options, context);
396 : }
397 22 : internal_file = estrdup(internal_file);
398 22 : php_url_free(resource);
399 22 : return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
400 : } else {
401 31 : int i_len = strlen(internal_file);
402 :
403 : /* search for directory */
404 31 : zend_hash_internal_pointer_reset(&phar->manifest);
405 285 : while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
406 253 : if (HASH_KEY_NON_EXISTANT !=
407 : zend_hash_get_current_key_ex(
408 : &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
409 253 : PHAR_STR(key, str_key);
410 253 : if (keylen > (uint)i_len && 0 == memcmp(str_key, internal_file, i_len)) {
411 : PHAR_STR_FREE(str_key);
412 : /* directory found */
413 30 : internal_file = estrndup(internal_file,
414 : i_len);
415 30 : php_url_free(resource);
416 30 : return phar_make_dirstream(internal_file, &phar->manifest TSRMLS_CC);
417 : }
418 : PHAR_STR_FREE(str_key);
419 : }
420 :
421 223 : if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
422 0 : break;
423 : }
424 : }
425 : }
426 :
427 1 : php_url_free(resource);
428 1 : return NULL;
429 : }
430 : /* }}} */
431 :
432 : /**
433 : * Make a new directory within a phar archive
434 : */
435 : int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
436 18 : {
437 : phar_entry_info entry, *e;
438 18 : phar_archive_data *phar = NULL;
439 : char *error, *arch, *entry2;
440 : int arch_len, entry_len;
441 18 : php_url *resource = NULL;
442 : uint host_len;
443 :
444 : /* pre-readonly check, we need to know if this is a data phar */
445 18 : if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) {
446 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", no phar archive specified", url_from);
447 2 : return 0;
448 : }
449 :
450 16 : if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) {
451 6 : phar = NULL;
452 : }
453 :
454 16 : efree(arch);
455 16 : efree(entry2);
456 :
457 16 : if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
458 1 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\", write operations disabled", url_from);
459 1 : return 0;
460 : }
461 :
462 15 : if ((resource = phar_parse_url(wrapper, url_from, "w", options TSRMLS_CC)) == NULL) {
463 1 : return 0;
464 : }
465 :
466 : /* we must have at the very least phar://alias.phar/internalfile.php */
467 14 : if (!resource->scheme || !resource->host || !resource->path) {
468 0 : php_url_free(resource);
469 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url_from);
470 0 : return 0;
471 : }
472 :
473 14 : if (strcasecmp("phar", resource->scheme)) {
474 0 : php_url_free(resource);
475 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url_from);
476 0 : return 0;
477 : }
478 :
479 14 : host_len = strlen(resource->host);
480 :
481 14 : if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
482 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", resource->path+1, resource->host, error);
483 0 : efree(error);
484 0 : php_url_free(resource);
485 0 : return 0;
486 : }
487 :
488 14 : if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 2, &error, 1 TSRMLS_CC))) {
489 : /* directory exists, or is a subdirectory of an existing file */
490 2 : if (e->is_temp_dir) {
491 1 : efree(e->filename);
492 1 : efree(e);
493 : }
494 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", resource->path+1, resource->host);
495 2 : php_url_free(resource);
496 2 : return 0;
497 : }
498 :
499 12 : if (error) {
500 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error);
501 2 : efree(error);
502 2 : php_url_free(resource);
503 2 : return 0;
504 : }
505 :
506 10 : if ((e = phar_get_entry_info_dir(phar, resource->path + 1, strlen(resource->path + 1), 0, &error, 1 TSRMLS_CC))) {
507 : /* entry exists as a file */
508 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", resource->path+1, resource->host);
509 0 : php_url_free(resource);
510 0 : return 0;
511 : }
512 :
513 10 : if (error) {
514 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error);
515 2 : efree(error);
516 2 : php_url_free(resource);
517 2 : return 0;
518 : }
519 :
520 8 : memset((void *) &entry, 0, sizeof(phar_entry_info));
521 :
522 : /* strip leading "/" */
523 8 : if (phar->is_zip) {
524 1 : entry.is_zip = 1;
525 : }
526 :
527 8 : entry.filename = estrdup(resource->path + 1);
528 :
529 8 : if (phar->is_tar) {
530 1 : entry.is_tar = 1;
531 1 : entry.tar_type = TAR_DIR;
532 : }
533 :
534 8 : entry.filename_len = strlen(resource->path + 1);
535 8 : php_url_free(resource);
536 8 : entry.is_dir = 1;
537 8 : entry.phar = phar;
538 8 : entry.is_modified = 1;
539 8 : entry.is_crc_checked = 1;
540 8 : entry.flags = PHAR_ENT_PERM_DEF_DIR;
541 8 : entry.old_flags = PHAR_ENT_PERM_DEF_DIR;
542 :
543 8 : if (SUCCESS != zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL)) {
544 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", entry.filename, phar->fname);
545 0 : efree(error);
546 0 : efree(entry.filename);
547 0 : return 0;
548 : }
549 :
550 8 : phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
551 :
552 8 : if (error) {
553 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry.filename, phar->fname, error);
554 0 : zend_hash_del(&phar->manifest, entry.filename, entry.filename_len);
555 0 : efree(error);
556 0 : return 0;
557 : }
558 :
559 8 : phar_add_virtual_dirs(phar, entry.filename, entry.filename_len TSRMLS_CC);
560 8 : return 1;
561 : }
562 : /* }}} */
563 :
564 : /**
565 : * Remove a directory within a phar archive
566 : */
567 : int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_stream_context *context TSRMLS_DC) /* {{{ */
568 18 : {
569 : phar_entry_info *entry;
570 18 : phar_archive_data *phar = NULL;
571 : char *error, *arch, *entry2;
572 : int arch_len, entry_len;
573 18 : php_url *resource = NULL;
574 : uint host_len;
575 : phar_zstr key;
576 : char *str_key;
577 : uint key_len;
578 : ulong unused;
579 : uint path_len;
580 :
581 : /* pre-readonly check, we need to know if this is a data phar */
582 18 : if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len, 2, 2 TSRMLS_CC)) {
583 2 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url);
584 2 : return 0;
585 : }
586 :
587 16 : if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL TSRMLS_CC)) {
588 1 : phar = NULL;
589 : }
590 :
591 16 : efree(arch);
592 16 : efree(entry2);
593 :
594 16 : if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
595 1 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot rmdir directory \"%s\", write operations disabled", url);
596 1 : return 0;
597 : }
598 :
599 15 : if ((resource = phar_parse_url(wrapper, url, "w", options TSRMLS_CC)) == NULL) {
600 0 : return 0;
601 : }
602 :
603 : /* we must have at the very least phar://alias.phar/internalfile.php */
604 15 : if (!resource->scheme || !resource->host || !resource->path) {
605 0 : php_url_free(resource);
606 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: invalid url \"%s\"", url);
607 0 : return 0;
608 : }
609 :
610 15 : if (strcasecmp("phar", resource->scheme)) {
611 0 : php_url_free(resource);
612 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: not a phar stream url \"%s\"", url);
613 0 : return 0;
614 : }
615 :
616 15 : host_len = strlen(resource->host);
617 :
618 15 : if (FAILURE == phar_get_archive(&phar, resource->host, host_len, NULL, 0, &error TSRMLS_CC)) {
619 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", resource->path+1, resource->host, error);
620 0 : efree(error);
621 0 : php_url_free(resource);
622 0 : return 0;
623 : }
624 :
625 15 : path_len = strlen(resource->path+1);
626 :
627 15 : if (!(entry = phar_get_entry_info_dir(phar, resource->path + 1, path_len, 2, &error, 1 TSRMLS_CC))) {
628 5 : if (error) {
629 1 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", resource->path+1, resource->host, error);
630 1 : efree(error);
631 : } else {
632 4 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", resource->path+1, resource->host);
633 : }
634 5 : php_url_free(resource);
635 5 : return 0;
636 : }
637 :
638 10 : if (!entry->is_deleted) {
639 10 : for (zend_hash_internal_pointer_reset(&phar->manifest);
640 37 : HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->manifest, &key, &key_len, &unused, 0, NULL);
641 17 : zend_hash_move_forward(&phar->manifest)) {
642 :
643 20 : PHAR_STR(key, str_key);
644 :
645 20 : if (key_len > path_len &&
646 : memcmp(str_key, resource->path+1, path_len) == 0 &&
647 : IS_SLASH(str_key[path_len])) {
648 : PHAR_STR_FREE(str_key);
649 3 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
650 3 : if (entry->is_temp_dir) {
651 1 : efree(entry->filename);
652 1 : efree(entry);
653 : }
654 3 : php_url_free(resource);
655 3 : return 0;
656 : }
657 : PHAR_STR_FREE(str_key);
658 : }
659 :
660 7 : for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
661 23 : HASH_KEY_NON_EXISTANT != zend_hash_get_current_key_ex(&phar->virtual_dirs, &key, &key_len, &unused, 0, NULL);
662 9 : zend_hash_move_forward(&phar->virtual_dirs)) {
663 :
664 9 : PHAR_STR(key, str_key);
665 :
666 9 : if (key_len > path_len &&
667 : memcmp(str_key, resource->path+1, path_len) == 0 &&
668 : IS_SLASH(str_key[path_len])) {
669 : PHAR_STR_FREE(str_key);
670 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: Directory not empty");
671 0 : if (entry->is_temp_dir) {
672 0 : efree(entry->filename);
673 0 : efree(entry);
674 : }
675 0 : php_url_free(resource);
676 0 : return 0;
677 : }
678 : PHAR_STR_FREE(str_key);
679 : }
680 : }
681 :
682 7 : if (entry->is_temp_dir) {
683 1 : zend_hash_del(&phar->virtual_dirs, resource->path+1, path_len);
684 1 : efree(entry->filename);
685 1 : efree(entry);
686 : } else {
687 6 : entry->is_deleted = 1;
688 6 : entry->is_modified = 1;
689 6 : phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
690 :
691 6 : if (error) {
692 0 : php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error);
693 0 : php_url_free(resource);
694 0 : efree(error);
695 0 : return 0;
696 : }
697 : }
698 :
699 7 : php_url_free(resource);
700 7 : return 1;
701 : }
702 : /* }}} */
703 :
704 : /*
705 : * Local variables:
706 : * tab-width: 4
707 : * c-basic-offset: 4
708 : * End:
709 : * vim600: noet sw=4 ts=4 fdm=marker
710 : * vim<600: noet sw=4 ts=4
711 : */
|