dirstream.c 20 KB

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