PageRenderTime 69ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/ext/phar/dirstream.c

http://github.com/php/php-src
C | 653 lines | 477 code | 90 blank | 86 comment | 152 complexity | cf79d8ae890a6f100d3fa62a4bd69d2e MD5 | raw file
Possible License(s): BSD-2-Clause, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1
  1. /*
  2. +----------------------------------------------------------------------+
  3. | phar:// stream wrapper support |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 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. #define PHAR_DIRSTREAM 1
  20. #include "phar_internal.h"
  21. #include "dirstream.h"
  22. BEGIN_EXTERN_C()
  23. void phar_dostat(phar_archive_data *phar, phar_entry_info *data, php_stream_statbuf *ssb, zend_bool is_dir);
  24. END_EXTERN_C()
  25. const php_stream_ops phar_dir_ops = {
  26. phar_dir_write, /* write */
  27. phar_dir_read, /* read */
  28. phar_dir_close, /* close */
  29. phar_dir_flush, /* flush */
  30. "phar dir",
  31. phar_dir_seek, /* seek */
  32. NULL, /* cast */
  33. NULL, /* stat */
  34. NULL, /* set option */
  35. };
  36. /**
  37. * Used for closedir($fp) where $fp is an opendir('phar://...') directory handle
  38. */
  39. static int phar_dir_close(php_stream *stream, int close_handle) /* {{{ */
  40. {
  41. HashTable *data = (HashTable *)stream->abstract;
  42. if (data) {
  43. zend_hash_destroy(data);
  44. FREE_HASHTABLE(data);
  45. stream->abstract = NULL;
  46. }
  47. return 0;
  48. }
  49. /* }}} */
  50. /**
  51. * Used for seeking on a phar directory handle
  52. */
  53. static int phar_dir_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) /* {{{ */
  54. {
  55. HashTable *data = (HashTable *)stream->abstract;
  56. if (!data) {
  57. return -1;
  58. }
  59. if (whence == SEEK_END) {
  60. whence = SEEK_SET;
  61. offset = zend_hash_num_elements(data) + offset;
  62. }
  63. if (whence == SEEK_SET) {
  64. zend_hash_internal_pointer_reset(data);
  65. }
  66. if (offset < 0) {
  67. return -1;
  68. } else {
  69. *newoffset = 0;
  70. while (*newoffset < offset && zend_hash_move_forward(data) == SUCCESS) {
  71. ++(*newoffset);
  72. }
  73. return 0;
  74. }
  75. }
  76. /* }}} */
  77. /**
  78. * Used for readdir() on an opendir()ed phar directory handle
  79. */
  80. static ssize_t phar_dir_read(php_stream *stream, char *buf, size_t count) /* {{{ */
  81. {
  82. size_t to_read;
  83. HashTable *data = (HashTable *)stream->abstract;
  84. zend_string *str_key;
  85. zend_ulong unused;
  86. if (HASH_KEY_NON_EXISTENT == zend_hash_get_current_key(data, &str_key, &unused)) {
  87. return 0;
  88. }
  89. zend_hash_move_forward(data);
  90. to_read = MIN(ZSTR_LEN(str_key), count);
  91. if (to_read == 0 || count < ZSTR_LEN(str_key)) {
  92. return 0;
  93. }
  94. memset(buf, 0, sizeof(php_stream_dirent));
  95. memcpy(((php_stream_dirent *) buf)->d_name, ZSTR_VAL(str_key), to_read);
  96. ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0';
  97. return sizeof(php_stream_dirent);
  98. }
  99. /* }}} */
  100. /**
  101. * Dummy: Used for writing to a phar directory (i.e. not used)
  102. */
  103. static ssize_t phar_dir_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
  104. {
  105. return -1;
  106. }
  107. /* }}} */
  108. /**
  109. * Dummy: Used for flushing writes to a phar directory (i.e. not used)
  110. */
  111. static int phar_dir_flush(php_stream *stream) /* {{{ */
  112. {
  113. return EOF;
  114. }
  115. /* }}} */
  116. /**
  117. * add an empty element with a char * key to a hash table, avoiding duplicates
  118. *
  119. * This is used to get a unique listing of virtual directories within a phar,
  120. * for iterating over opendir()ed phar directories.
  121. */
  122. static int phar_add_empty(HashTable *ht, char *arKey, uint32_t nKeyLength) /* {{{ */
  123. {
  124. zval dummy;
  125. ZVAL_NULL(&dummy);
  126. zend_hash_str_update(ht, arKey, nKeyLength, &dummy);
  127. return SUCCESS;
  128. }
  129. /* }}} */
  130. /**
  131. * Used for sorting directories alphabetically
  132. */
  133. static int phar_compare_dir_name(Bucket *f, Bucket *s) /* {{{ */
  134. {
  135. int result = zend_binary_strcmp(
  136. ZSTR_VAL(f->key), ZSTR_LEN(f->key), ZSTR_VAL(s->key), ZSTR_LEN(s->key));
  137. return ZEND_NORMALIZE_BOOL(result);
  138. }
  139. /* }}} */
  140. /**
  141. * Create a opendir() directory stream handle by iterating over each of the
  142. * files in a phar and retrieving its relative path. From this, construct
  143. * a list of files/directories that are "in" the directory represented by dir
  144. */
  145. static php_stream *phar_make_dirstream(char *dir, HashTable *manifest) /* {{{ */
  146. {
  147. HashTable *data;
  148. size_t dirlen = strlen(dir);
  149. char *entry, *found, *save;
  150. zend_string *str_key;
  151. size_t keylen;
  152. zend_ulong unused;
  153. ALLOC_HASHTABLE(data);
  154. zend_hash_init(data, 64, NULL, NULL, 0);
  155. if ((*dir == '/' && dirlen == 1 && (manifest->nNumOfElements == 0)) || (dirlen >= sizeof(".phar")-1 && !memcmp(dir, ".phar", sizeof(".phar")-1))) {
  156. /* make empty root directory for empty phar */
  157. /* make empty directory for .phar magic directory */
  158. efree(dir);
  159. return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
  160. }
  161. zend_hash_internal_pointer_reset(manifest);
  162. while (FAILURE != zend_hash_has_more_elements(manifest)) {
  163. if (HASH_KEY_NON_EXISTENT == zend_hash_get_current_key(manifest, &str_key, &unused)) {
  164. break;
  165. }
  166. keylen = ZSTR_LEN(str_key);
  167. if (keylen <= dirlen) {
  168. if (keylen == 0 || keylen < dirlen || !strncmp(ZSTR_VAL(str_key), dir, dirlen)) {
  169. if (SUCCESS != zend_hash_move_forward(manifest)) {
  170. break;
  171. }
  172. continue;
  173. }
  174. }
  175. if (*dir == '/') {
  176. /* root directory */
  177. if (keylen >= sizeof(".phar")-1 && !memcmp(ZSTR_VAL(str_key), ".phar", sizeof(".phar")-1)) {
  178. /* do not add any magic entries to this directory */
  179. if (SUCCESS != zend_hash_move_forward(manifest)) {
  180. break;
  181. }
  182. continue;
  183. }
  184. if (NULL != (found = (char *) memchr(ZSTR_VAL(str_key), '/', keylen))) {
  185. /* the entry has a path separator and is a subdirectory */
  186. entry = (char *) safe_emalloc(found - ZSTR_VAL(str_key), 1, 1);
  187. memcpy(entry, ZSTR_VAL(str_key), found - ZSTR_VAL(str_key));
  188. keylen = found - ZSTR_VAL(str_key);
  189. entry[keylen] = '\0';
  190. } else {
  191. entry = (char *) safe_emalloc(keylen, 1, 1);
  192. memcpy(entry, ZSTR_VAL(str_key), keylen);
  193. entry[keylen] = '\0';
  194. }
  195. goto PHAR_ADD_ENTRY;
  196. } else {
  197. if (0 != memcmp(ZSTR_VAL(str_key), dir, dirlen)) {
  198. /* entry in directory not found */
  199. if (SUCCESS != zend_hash_move_forward(manifest)) {
  200. break;
  201. }
  202. continue;
  203. } else {
  204. if (ZSTR_VAL(str_key)[dirlen] != '/') {
  205. if (SUCCESS != zend_hash_move_forward(manifest)) {
  206. break;
  207. }
  208. continue;
  209. }
  210. }
  211. }
  212. save = ZSTR_VAL(str_key);
  213. save += dirlen + 1; /* seek to just past the path separator */
  214. if (NULL != (found = (char *) memchr(save, '/', keylen - dirlen - 1))) {
  215. /* is subdirectory */
  216. save -= dirlen + 1;
  217. entry = (char *) safe_emalloc(found - save + dirlen, 1, 1);
  218. memcpy(entry, save + dirlen + 1, found - save - dirlen - 1);
  219. keylen = found - save - dirlen - 1;
  220. entry[keylen] = '\0';
  221. } else {
  222. /* is file */
  223. save -= dirlen + 1;
  224. entry = (char *) safe_emalloc(keylen - dirlen, 1, 1);
  225. memcpy(entry, save + dirlen + 1, keylen - dirlen - 1);
  226. entry[keylen - dirlen - 1] = '\0';
  227. keylen = keylen - dirlen - 1;
  228. }
  229. PHAR_ADD_ENTRY:
  230. if (keylen) {
  231. phar_add_empty(data, entry, keylen);
  232. }
  233. efree(entry);
  234. if (SUCCESS != zend_hash_move_forward(manifest)) {
  235. break;
  236. }
  237. }
  238. if (FAILURE != zend_hash_has_more_elements(data)) {
  239. efree(dir);
  240. zend_hash_sort(data, phar_compare_dir_name, 0);
  241. return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
  242. } else {
  243. efree(dir);
  244. return php_stream_alloc(&phar_dir_ops, data, NULL, "r");
  245. }
  246. }
  247. /* }}}*/
  248. /**
  249. * Open a directory handle within a phar archive
  250. */
  251. php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
  252. {
  253. php_url *resource = NULL;
  254. php_stream *ret;
  255. char *internal_file, *error;
  256. zend_string *str_key;
  257. zend_ulong unused;
  258. phar_archive_data *phar;
  259. phar_entry_info *entry = NULL;
  260. uint32_t host_len;
  261. if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) {
  262. php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path);
  263. return NULL;
  264. }
  265. /* we must have at the very least phar://alias.phar/ */
  266. if (!resource->scheme || !resource->host || !resource->path) {
  267. if (resource->host && !resource->path) {
  268. php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host));
  269. php_url_free(resource);
  270. return NULL;
  271. }
  272. php_url_free(resource);
  273. php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
  274. return NULL;
  275. }
  276. if (!zend_string_equals_literal_ci(resource->scheme, "phar")) {
  277. php_url_free(resource);
  278. php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar url \"%s\"", path);
  279. return NULL;
  280. }
  281. host_len = ZSTR_LEN(resource->host);
  282. phar_request_initialize();
  283. internal_file = ZSTR_VAL(resource->path) + 1; /* strip leading "/" */
  284. if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), host_len, NULL, 0, &error)) {
  285. if (error) {
  286. php_stream_wrapper_log_error(wrapper, options, "%s", error);
  287. efree(error);
  288. } else {
  289. php_stream_wrapper_log_error(wrapper, options, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host));
  290. }
  291. php_url_free(resource);
  292. return NULL;
  293. }
  294. if (error) {
  295. efree(error);
  296. }
  297. if (*internal_file == '\0') {
  298. /* root directory requested */
  299. internal_file = estrndup(internal_file - 1, 1);
  300. ret = phar_make_dirstream(internal_file, &phar->manifest);
  301. php_url_free(resource);
  302. return ret;
  303. }
  304. if (!HT_IS_INITIALIZED(&phar->manifest)) {
  305. php_url_free(resource);
  306. return NULL;
  307. }
  308. if (NULL != (entry = zend_hash_str_find_ptr(&phar->manifest, internal_file, strlen(internal_file))) && !entry->is_dir) {
  309. php_url_free(resource);
  310. return NULL;
  311. } else if (entry && entry->is_dir) {
  312. if (entry->is_mounted) {
  313. php_url_free(resource);
  314. return php_stream_opendir(entry->tmp, options, context);
  315. }
  316. internal_file = estrdup(internal_file);
  317. php_url_free(resource);
  318. return phar_make_dirstream(internal_file, &phar->manifest);
  319. } else {
  320. size_t i_len = strlen(internal_file);
  321. /* search for directory */
  322. zend_hash_internal_pointer_reset(&phar->manifest);
  323. while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
  324. if (HASH_KEY_NON_EXISTENT !=
  325. zend_hash_get_current_key(&phar->manifest, &str_key, &unused)) {
  326. if (ZSTR_LEN(str_key) > i_len && 0 == memcmp(ZSTR_VAL(str_key), internal_file, i_len)) {
  327. /* directory found */
  328. internal_file = estrndup(internal_file,
  329. i_len);
  330. php_url_free(resource);
  331. return phar_make_dirstream(internal_file, &phar->manifest);
  332. }
  333. }
  334. if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
  335. break;
  336. }
  337. }
  338. }
  339. php_url_free(resource);
  340. return NULL;
  341. }
  342. /* }}} */
  343. /**
  344. * Make a new directory within a phar archive
  345. */
  346. int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mode, int options, php_stream_context *context) /* {{{ */
  347. {
  348. phar_entry_info entry, *e;
  349. phar_archive_data *phar = NULL;
  350. char *error, *arch, *entry2;
  351. size_t arch_len, entry_len;
  352. php_url *resource = NULL;
  353. uint32_t host_len;
  354. /* pre-readonly check, we need to know if this is a data phar */
  355. if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, &entry2, &entry_len, 2, 2)) {
  356. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from);
  357. return 0;
  358. }
  359. if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
  360. phar = NULL;
  361. }
  362. efree(arch);
  363. efree(entry2);
  364. if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
  365. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", write operations disabled", url_from);
  366. return 0;
  367. }
  368. if ((resource = phar_parse_url(wrapper, url_from, "w", options)) == NULL) {
  369. return 0;
  370. }
  371. /* we must have at the very least phar://alias.phar/internalfile.php */
  372. if (!resource->scheme || !resource->host || !resource->path) {
  373. php_url_free(resource);
  374. php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url_from);
  375. return 0;
  376. }
  377. if (!zend_string_equals_literal_ci(resource->scheme, "phar")) {
  378. php_url_free(resource);
  379. php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from);
  380. return 0;
  381. }
  382. host_len = ZSTR_LEN(resource->host);
  383. if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), host_len, NULL, 0, &error)) {
  384. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error);
  385. efree(error);
  386. php_url_free(resource);
  387. return 0;
  388. }
  389. if ((e = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 2, &error, 1))) {
  390. /* directory exists, or is a subdirectory of an existing file */
  391. if (e->is_temp_dir) {
  392. efree(e->filename);
  393. efree(e);
  394. }
  395. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
  396. php_url_free(resource);
  397. return 0;
  398. }
  399. if (error) {
  400. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
  401. efree(error);
  402. php_url_free(resource);
  403. return 0;
  404. }
  405. if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, 1)) {
  406. /* entry exists as a file */
  407. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
  408. php_url_free(resource);
  409. return 0;
  410. }
  411. if (error) {
  412. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
  413. efree(error);
  414. php_url_free(resource);
  415. return 0;
  416. }
  417. memset((void *) &entry, 0, sizeof(phar_entry_info));
  418. /* strip leading "/" */
  419. if (phar->is_zip) {
  420. entry.is_zip = 1;
  421. }
  422. entry.filename = estrdup(ZSTR_VAL(resource->path) + 1);
  423. if (phar->is_tar) {
  424. entry.is_tar = 1;
  425. entry.tar_type = TAR_DIR;
  426. }
  427. entry.filename_len = ZSTR_LEN(resource->path) - 1;
  428. php_url_free(resource);
  429. entry.is_dir = 1;
  430. entry.phar = phar;
  431. entry.is_modified = 1;
  432. entry.is_crc_checked = 1;
  433. entry.flags = PHAR_ENT_PERM_DEF_DIR;
  434. entry.old_flags = PHAR_ENT_PERM_DEF_DIR;
  435. if (NULL == zend_hash_str_add_mem(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info))) {
  436. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", entry.filename, phar->fname);
  437. efree(error);
  438. efree(entry.filename);
  439. return 0;
  440. }
  441. phar_flush(phar, 0, 0, 0, &error);
  442. if (error) {
  443. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry.filename, phar->fname, error);
  444. zend_hash_str_del(&phar->manifest, entry.filename, entry.filename_len);
  445. efree(error);
  446. return 0;
  447. }
  448. phar_add_virtual_dirs(phar, entry.filename, entry.filename_len);
  449. return 1;
  450. }
  451. /* }}} */
  452. /**
  453. * Remove a directory within a phar archive
  454. */
  455. int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context) /* {{{ */
  456. {
  457. phar_entry_info *entry;
  458. phar_archive_data *phar = NULL;
  459. char *error, *arch, *entry2;
  460. size_t arch_len, entry_len;
  461. php_url *resource = NULL;
  462. uint32_t host_len;
  463. zend_string *str_key;
  464. zend_ulong unused;
  465. uint32_t path_len;
  466. /* pre-readonly check, we need to know if this is a data phar */
  467. if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, &entry2, &entry_len, 2, 2)) {
  468. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url);
  469. return 0;
  470. }
  471. if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
  472. phar = NULL;
  473. }
  474. efree(arch);
  475. efree(entry2);
  476. if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
  477. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot rmdir directory \"%s\", write operations disabled", url);
  478. return 0;
  479. }
  480. if ((resource = phar_parse_url(wrapper, url, "w", options)) == NULL) {
  481. return 0;
  482. }
  483. /* we must have at the very least phar://alias.phar/internalfile.php */
  484. if (!resource->scheme || !resource->host || !resource->path) {
  485. php_url_free(resource);
  486. php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url);
  487. return 0;
  488. }
  489. if (!zend_string_equals_literal_ci(resource->scheme, "phar")) {
  490. php_url_free(resource);
  491. php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url);
  492. return 0;
  493. }
  494. host_len = ZSTR_LEN(resource->host);
  495. if (FAILURE == phar_get_archive(&phar, ZSTR_VAL(resource->host), host_len, NULL, 0, &error)) {
  496. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
  497. efree(error);
  498. php_url_free(resource);
  499. return 0;
  500. }
  501. path_len = ZSTR_LEN(resource->path) - 1;
  502. if (!(entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, 1))) {
  503. if (error) {
  504. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
  505. efree(error);
  506. } else {
  507. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
  508. }
  509. php_url_free(resource);
  510. return 0;
  511. }
  512. if (!entry->is_deleted) {
  513. for (zend_hash_internal_pointer_reset(&phar->manifest);
  514. HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&phar->manifest, &str_key, &unused);
  515. zend_hash_move_forward(&phar->manifest)
  516. ) {
  517. if (ZSTR_LEN(str_key) > path_len &&
  518. memcmp(ZSTR_VAL(str_key), ZSTR_VAL(resource->path)+1, path_len) == 0 &&
  519. IS_SLASH(ZSTR_VAL(str_key)[path_len])) {
  520. php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty");
  521. if (entry->is_temp_dir) {
  522. efree(entry->filename);
  523. efree(entry);
  524. }
  525. php_url_free(resource);
  526. return 0;
  527. }
  528. }
  529. for (zend_hash_internal_pointer_reset(&phar->virtual_dirs);
  530. HASH_KEY_NON_EXISTENT != zend_hash_get_current_key(&phar->virtual_dirs, &str_key, &unused);
  531. zend_hash_move_forward(&phar->virtual_dirs)) {
  532. if (ZSTR_LEN(str_key) > path_len &&
  533. memcmp(ZSTR_VAL(str_key), ZSTR_VAL(resource->path)+1, path_len) == 0 &&
  534. IS_SLASH(ZSTR_VAL(str_key)[path_len])) {
  535. php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty");
  536. if (entry->is_temp_dir) {
  537. efree(entry->filename);
  538. efree(entry);
  539. }
  540. php_url_free(resource);
  541. return 0;
  542. }
  543. }
  544. }
  545. if (entry->is_temp_dir) {
  546. zend_hash_str_del(&phar->virtual_dirs, ZSTR_VAL(resource->path)+1, path_len);
  547. efree(entry->filename);
  548. efree(entry);
  549. } else {
  550. entry->is_deleted = 1;
  551. entry->is_modified = 1;
  552. phar_flush(phar, 0, 0, 0, &error);
  553. if (error) {
  554. php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error);
  555. php_url_free(resource);
  556. efree(error);
  557. return 0;
  558. }
  559. }
  560. php_url_free(resource);
  561. return 1;
  562. }
  563. /* }}} */