PageRenderTime 97ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/ext/phar/stream.c

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