php_fopen_wrapper.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Authors: Rasmus Lerdorf <rasmus@php.net> |
  14. | Jim Winstead <jimw@php.net> |
  15. | Hartmut Holzgraefe <hholzgra@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #if HAVE_UNISTD_H
  21. #include <unistd.h>
  22. #endif
  23. #include "php.h"
  24. #include "php_globals.h"
  25. #include "php_standard.h"
  26. #include "php_memory_streams.h"
  27. #include "php_fopen_wrappers.h"
  28. #include "SAPI.h"
  29. static ssize_t php_stream_output_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
  30. {
  31. PHPWRITE(buf, count);
  32. return count;
  33. }
  34. /* }}} */
  35. static ssize_t php_stream_output_read(php_stream *stream, char *buf, size_t count) /* {{{ */
  36. {
  37. stream->eof = 1;
  38. return -1;
  39. }
  40. /* }}} */
  41. static int php_stream_output_close(php_stream *stream, int close_handle) /* {{{ */
  42. {
  43. return 0;
  44. }
  45. /* }}} */
  46. const php_stream_ops php_stream_output_ops = {
  47. php_stream_output_write,
  48. php_stream_output_read,
  49. php_stream_output_close,
  50. NULL, /* flush */
  51. "Output",
  52. NULL, /* seek */
  53. NULL, /* cast */
  54. NULL, /* stat */
  55. NULL /* set_option */
  56. };
  57. typedef struct php_stream_input { /* {{{ */
  58. php_stream *body;
  59. zend_off_t position;
  60. } php_stream_input_t;
  61. /* }}} */
  62. static ssize_t php_stream_input_write(php_stream *stream, const char *buf, size_t count) /* {{{ */
  63. {
  64. return -1;
  65. }
  66. /* }}} */
  67. static ssize_t php_stream_input_read(php_stream *stream, char *buf, size_t count) /* {{{ */
  68. {
  69. php_stream_input_t *input = stream->abstract;
  70. ssize_t read;
  71. if (!SG(post_read) && SG(read_post_bytes) < (int64_t)(input->position + count)) {
  72. /* read requested data from SAPI */
  73. size_t read_bytes = sapi_read_post_block(buf, count);
  74. if (read_bytes > 0) {
  75. php_stream_seek(input->body, 0, SEEK_END);
  76. php_stream_write(input->body, buf, read_bytes);
  77. }
  78. }
  79. if (!input->body->readfilters.head) {
  80. /* If the input stream contains filters, it's not really seekable. The
  81. input->position is likely to be wrong for unfiltered data. */
  82. php_stream_seek(input->body, input->position, SEEK_SET);
  83. }
  84. read = php_stream_read(input->body, buf, count);
  85. if (!read || read == (size_t) -1) {
  86. stream->eof = 1;
  87. } else {
  88. input->position += read;
  89. }
  90. return read;
  91. }
  92. /* }}} */
  93. static int php_stream_input_close(php_stream *stream, int close_handle) /* {{{ */
  94. {
  95. efree(stream->abstract);
  96. stream->abstract = NULL;
  97. return 0;
  98. }
  99. /* }}} */
  100. static int php_stream_input_flush(php_stream *stream) /* {{{ */
  101. {
  102. return -1;
  103. }
  104. /* }}} */
  105. static int php_stream_input_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffset) /* {{{ */
  106. {
  107. php_stream_input_t *input = stream->abstract;
  108. if (input->body) {
  109. int sought = php_stream_seek(input->body, offset, whence);
  110. *newoffset = input->position = (input->body)->position;
  111. return sought;
  112. }
  113. return -1;
  114. }
  115. /* }}} */
  116. const php_stream_ops php_stream_input_ops = {
  117. php_stream_input_write,
  118. php_stream_input_read,
  119. php_stream_input_close,
  120. php_stream_input_flush,
  121. "Input",
  122. php_stream_input_seek,
  123. NULL, /* cast */
  124. NULL, /* stat */
  125. NULL /* set_option */
  126. };
  127. static void php_stream_apply_filter_list(php_stream *stream, char *filterlist, int read_chain, int write_chain) /* {{{ */
  128. {
  129. char *p, *token = NULL;
  130. php_stream_filter *temp_filter;
  131. p = php_strtok_r(filterlist, "|", &token);
  132. while (p) {
  133. php_url_decode(p, strlen(p));
  134. if (read_chain) {
  135. if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {
  136. php_stream_filter_append(&stream->readfilters, temp_filter);
  137. } else {
  138. php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);
  139. }
  140. }
  141. if (write_chain) {
  142. if ((temp_filter = php_stream_filter_create(p, NULL, php_stream_is_persistent(stream)))) {
  143. php_stream_filter_append(&stream->writefilters, temp_filter);
  144. } else {
  145. php_error_docref(NULL, E_WARNING, "Unable to create filter (%s)", p);
  146. }
  147. }
  148. p = php_strtok_r(NULL, "|", &token);
  149. }
  150. }
  151. /* }}} */
  152. php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,
  153. zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
  154. {
  155. int fd = -1;
  156. int mode_rw = 0;
  157. php_stream * stream = NULL;
  158. char *p, *token = NULL, *pathdup;
  159. zend_long max_memory;
  160. FILE *file = NULL;
  161. #ifdef PHP_WIN32
  162. int pipe_requested = 0;
  163. #endif
  164. if (!strncasecmp(path, "php://", 6)) {
  165. path += 6;
  166. }
  167. if (!strncasecmp(path, "temp", 4)) {
  168. path += 4;
  169. max_memory = PHP_STREAM_MAX_MEM;
  170. if (!strncasecmp(path, "/maxmemory:", 11)) {
  171. path += 11;
  172. max_memory = ZEND_STRTOL(path, NULL, 10);
  173. if (max_memory < 0) {
  174. zend_argument_value_error(2, "must be greater than or equal to 0");
  175. return NULL;
  176. }
  177. }
  178. mode_rw = php_stream_mode_from_str(mode);
  179. return php_stream_temp_create(mode_rw, max_memory);
  180. }
  181. if (!strcasecmp(path, "memory")) {
  182. mode_rw = php_stream_mode_from_str(mode);
  183. return php_stream_memory_create(mode_rw);
  184. }
  185. if (!strcasecmp(path, "output")) {
  186. return php_stream_alloc(&php_stream_output_ops, NULL, 0, "wb");
  187. }
  188. if (!strcasecmp(path, "input")) {
  189. php_stream_input_t *input;
  190. if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
  191. if (options & REPORT_ERRORS) {
  192. php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration");
  193. }
  194. return NULL;
  195. }
  196. input = ecalloc(1, sizeof(*input));
  197. if ((input->body = SG(request_info).request_body)) {
  198. php_stream_rewind(input->body);
  199. } else {
  200. input->body = php_stream_temp_create_ex(TEMP_STREAM_DEFAULT, SAPI_POST_BLOCK_SIZE, PG(upload_tmp_dir));
  201. SG(request_info).request_body = input->body;
  202. }
  203. return php_stream_alloc(&php_stream_input_ops, input, 0, "rb");
  204. }
  205. if (!strcasecmp(path, "stdin")) {
  206. if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
  207. if (options & REPORT_ERRORS) {
  208. php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration");
  209. }
  210. return NULL;
  211. }
  212. if (!strcmp(sapi_module.name, "cli")) {
  213. static int cli_in = 0;
  214. fd = STDIN_FILENO;
  215. if (cli_in) {
  216. fd = dup(fd);
  217. } else {
  218. cli_in = 1;
  219. file = stdin;
  220. }
  221. } else {
  222. fd = dup(STDIN_FILENO);
  223. }
  224. #ifdef PHP_WIN32
  225. pipe_requested = 1;
  226. #endif
  227. } else if (!strcasecmp(path, "stdout")) {
  228. if (!strcmp(sapi_module.name, "cli")) {
  229. static int cli_out = 0;
  230. fd = STDOUT_FILENO;
  231. if (cli_out++) {
  232. fd = dup(fd);
  233. } else {
  234. cli_out = 1;
  235. file = stdout;
  236. }
  237. } else {
  238. fd = dup(STDOUT_FILENO);
  239. }
  240. #ifdef PHP_WIN32
  241. pipe_requested = 1;
  242. #endif
  243. } else if (!strcasecmp(path, "stderr")) {
  244. if (!strcmp(sapi_module.name, "cli")) {
  245. static int cli_err = 0;
  246. fd = STDERR_FILENO;
  247. if (cli_err++) {
  248. fd = dup(fd);
  249. } else {
  250. cli_err = 1;
  251. file = stderr;
  252. }
  253. } else {
  254. fd = dup(STDERR_FILENO);
  255. }
  256. #ifdef PHP_WIN32
  257. pipe_requested = 1;
  258. #endif
  259. } else if (!strncasecmp(path, "fd/", 3)) {
  260. const char *start;
  261. char *end;
  262. zend_long fildes_ori;
  263. int dtablesize;
  264. if (strcmp(sapi_module.name, "cli")) {
  265. if (options & REPORT_ERRORS) {
  266. php_error_docref(NULL, E_WARNING, "Direct access to file descriptors is only available from command-line PHP");
  267. }
  268. return NULL;
  269. }
  270. if ((options & STREAM_OPEN_FOR_INCLUDE) && !PG(allow_url_include) ) {
  271. if (options & REPORT_ERRORS) {
  272. php_error_docref(NULL, E_WARNING, "URL file-access is disabled in the server configuration");
  273. }
  274. return NULL;
  275. }
  276. start = &path[3];
  277. fildes_ori = ZEND_STRTOL(start, &end, 10);
  278. if (end == start || *end != '\0') {
  279. php_stream_wrapper_log_error(wrapper, options,
  280. "php://fd/ stream must be specified in the form php://fd/<orig fd>");
  281. return NULL;
  282. }
  283. #if HAVE_UNISTD_H
  284. dtablesize = getdtablesize();
  285. #else
  286. dtablesize = INT_MAX;
  287. #endif
  288. if (fildes_ori < 0 || fildes_ori >= dtablesize) {
  289. php_stream_wrapper_log_error(wrapper, options,
  290. "The file descriptors must be non-negative numbers smaller than %d", dtablesize);
  291. return NULL;
  292. }
  293. fd = dup((int)fildes_ori);
  294. if (fd == -1) {
  295. php_stream_wrapper_log_error(wrapper, options,
  296. "Error duping file descriptor " ZEND_LONG_FMT "; possibly it doesn't exist: "
  297. "[%d]: %s", fildes_ori, errno, strerror(errno));
  298. return NULL;
  299. }
  300. } else if (!strncasecmp(path, "filter/", 7)) {
  301. /* Save time/memory when chain isn't specified */
  302. if (strchr(mode, 'r') || strchr(mode, '+')) {
  303. mode_rw |= PHP_STREAM_FILTER_READ;
  304. }
  305. if (strchr(mode, 'w') || strchr(mode, '+') || strchr(mode, 'a')) {
  306. mode_rw |= PHP_STREAM_FILTER_WRITE;
  307. }
  308. pathdup = estrndup(path + 6, strlen(path + 6));
  309. p = strstr(pathdup, "/resource=");
  310. if (!p) {
  311. zend_throw_error(NULL, "No URL resource specified");
  312. efree(pathdup);
  313. return NULL;
  314. }
  315. if (!(stream = php_stream_open_wrapper(p + 10, mode, options, opened_path))) {
  316. efree(pathdup);
  317. return NULL;
  318. }
  319. *p = '\0';
  320. p = php_strtok_r(pathdup + 1, "/", &token);
  321. while (p) {
  322. if (!strncasecmp(p, "read=", 5)) {
  323. php_stream_apply_filter_list(stream, p + 5, 1, 0);
  324. } else if (!strncasecmp(p, "write=", 6)) {
  325. php_stream_apply_filter_list(stream, p + 6, 0, 1);
  326. } else {
  327. php_stream_apply_filter_list(stream, p, mode_rw & PHP_STREAM_FILTER_READ, mode_rw & PHP_STREAM_FILTER_WRITE);
  328. }
  329. p = php_strtok_r(NULL, "/", &token);
  330. }
  331. efree(pathdup);
  332. if (EG(exception)) {
  333. php_stream_close(stream);
  334. return NULL;
  335. }
  336. return stream;
  337. } else {
  338. /* invalid php://thingy */
  339. php_error_docref(NULL, E_WARNING, "Invalid php:// URL specified");
  340. return NULL;
  341. }
  342. /* must be stdin, stderr or stdout */
  343. if (fd == -1) {
  344. /* failed to dup */
  345. return NULL;
  346. }
  347. #if defined(S_IFSOCK) && !defined(PHP_WIN32)
  348. do {
  349. zend_stat_t st;
  350. memset(&st, 0, sizeof(st));
  351. if (zend_fstat(fd, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
  352. stream = php_stream_sock_open_from_socket(fd, NULL);
  353. if (stream) {
  354. stream->ops = &php_stream_socket_ops;
  355. return stream;
  356. }
  357. }
  358. } while (0);
  359. #endif
  360. if (file) {
  361. stream = php_stream_fopen_from_file(file, mode);
  362. } else {
  363. stream = php_stream_fopen_from_fd(fd, mode, NULL);
  364. if (stream == NULL) {
  365. close(fd);
  366. }
  367. }
  368. #ifdef PHP_WIN32
  369. if (pipe_requested && stream && context) {
  370. zval *blocking_pipes = php_stream_context_get_option(context, "pipe", "blocking");
  371. if (blocking_pipes) {
  372. php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, zval_get_long(blocking_pipes), NULL);
  373. }
  374. }
  375. #endif
  376. return stream;
  377. }
  378. /* }}} */
  379. static const php_stream_wrapper_ops php_stdio_wops = {
  380. php_stream_url_wrap_php,
  381. NULL, /* close */
  382. NULL, /* fstat */
  383. NULL, /* stat */
  384. NULL, /* opendir */
  385. "PHP",
  386. NULL, /* unlink */
  387. NULL, /* rename */
  388. NULL, /* mkdir */
  389. NULL, /* rmdir */
  390. NULL
  391. };
  392. PHPAPI const php_stream_wrapper php_stream_php_wrapper = {
  393. &php_stdio_wops,
  394. NULL,
  395. 0, /* is_url */
  396. };