zend_stream.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. |
  11. | If you did not receive a copy of the Zend license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@zend.com so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Authors: Wez Furlong <wez@thebrainroom.com> |
  16. | Scott MacVicar <scottmac@php.net> |
  17. | Nuno Lopes <nlopess@php.net> |
  18. | Marcus Boerger <helly@php.net> |
  19. +----------------------------------------------------------------------+
  20. */
  21. /* $Id$ */
  22. #include "zend.h"
  23. #include "zend_compile.h"
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #if HAVE_MMAP
  27. # if HAVE_UNISTD_H
  28. # include <unistd.h>
  29. # if defined(_SC_PAGESIZE)
  30. # define REAL_PAGE_SIZE sysconf(_SC_PAGESIZE);
  31. # elif defined(_SC_PAGE_SIZE)
  32. # define REAL_PAGE_SIZE sysconf(_SC_PAGE_SIZE);
  33. # endif
  34. # endif
  35. # if HAVE_SYS_MMAN_H
  36. # include <sys/mman.h>
  37. # endif
  38. # ifndef REAL_PAGE_SIZE
  39. # ifdef PAGE_SIZE
  40. # define REAL_PAGE_SIZE PAGE_SIZE
  41. # else
  42. # define REAL_PAGE_SIZE 4096
  43. # endif
  44. # endif
  45. #endif
  46. ZEND_DLIMPORT int isatty(int fd);
  47. static size_t zend_stream_stdio_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
  48. {
  49. return fread(buf, 1, len, (FILE*)handle);
  50. } /* }}} */
  51. static void zend_stream_stdio_closer(void *handle TSRMLS_DC) /* {{{ */
  52. {
  53. if (handle && (FILE*)handle != stdin) {
  54. fclose((FILE*)handle);
  55. }
  56. } /* }}} */
  57. static size_t zend_stream_stdio_fsizer(void *handle TSRMLS_DC) /* {{{ */
  58. {
  59. struct stat buf;
  60. if (handle && fstat(fileno((FILE*)handle), &buf) == 0) {
  61. #ifdef S_ISREG
  62. if (!S_ISREG(buf.st_mode)) {
  63. return 0;
  64. }
  65. #endif
  66. return buf.st_size;
  67. }
  68. return 0;
  69. } /* }}} */
  70. static void zend_stream_unmap(zend_stream *stream TSRMLS_DC) { /* {{{ */
  71. #if HAVE_MMAP
  72. if (stream->mmap.map) {
  73. munmap(stream->mmap.map, stream->mmap.len + ZEND_MMAP_AHEAD);
  74. } else
  75. #endif
  76. if (stream->mmap.buf) {
  77. efree(stream->mmap.buf);
  78. }
  79. stream->mmap.len = 0;
  80. stream->mmap.pos = 0;
  81. stream->mmap.map = 0;
  82. stream->mmap.buf = 0;
  83. stream->handle = stream->mmap.old_handle;
  84. } /* }}} */
  85. static void zend_stream_mmap_closer(zend_stream *stream TSRMLS_DC) /* {{{ */
  86. {
  87. zend_stream_unmap(stream TSRMLS_CC);
  88. if (stream->mmap.old_closer && stream->handle) {
  89. stream->mmap.old_closer(stream->handle TSRMLS_CC);
  90. }
  91. } /* }}} */
  92. static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */
  93. return file_handle->type == ZEND_HANDLE_MAPPED;
  94. } /* }}} */
  95. static size_t zend_stream_fsize(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */
  96. {
  97. struct stat buf;
  98. if (zend_stream_is_mmap(file_handle)) {
  99. return file_handle->handle.stream.mmap.len;
  100. }
  101. if (file_handle->type == ZEND_HANDLE_STREAM || file_handle->type == ZEND_HANDLE_MAPPED) {
  102. return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle TSRMLS_CC);
  103. }
  104. if (file_handle->handle.fp && fstat(fileno(file_handle->handle.fp), &buf) == 0) {
  105. #ifdef S_ISREG
  106. if (!S_ISREG(buf.st_mode)) {
  107. return 0;
  108. }
  109. #endif
  110. return buf.st_size;
  111. }
  112. return -1;
  113. } /* }}} */
  114. ZEND_API int zend_stream_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */
  115. {
  116. if (zend_stream_open_function) {
  117. return zend_stream_open_function(filename, handle TSRMLS_CC);
  118. }
  119. handle->type = ZEND_HANDLE_FP;
  120. handle->opened_path = NULL;
  121. handle->handle.fp = zend_fopen(filename, &handle->opened_path TSRMLS_CC);
  122. handle->filename = filename;
  123. handle->free_filename = 0;
  124. memset(&handle->handle.stream.mmap, 0, sizeof(zend_mmap));
  125. return (handle->handle.fp) ? SUCCESS : FAILURE;
  126. } /* }}} */
  127. static int zend_stream_getc(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */
  128. {
  129. char buf;
  130. if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf) TSRMLS_CC)) {
  131. return (int)buf;
  132. }
  133. return EOF;
  134. } /* }}} */
  135. static size_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
  136. {
  137. if (!zend_stream_is_mmap(file_handle) && file_handle->handle.stream.isatty) {
  138. int c = '*';
  139. size_t n;
  140. #ifdef NETWARE
  141. /*
  142. c != 4 check is there as fread of a character in NetWare LibC gives 4 upon ^D character.
  143. Ascii value 4 is actually EOT character which is not defined anywhere in the LibC
  144. or else we can use instead of hardcoded 4.
  145. */
  146. for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != 4 && c != '\n'; ++n) {
  147. #else
  148. for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != '\n'; ++n) {
  149. #endif
  150. buf[n] = (char)c;
  151. }
  152. if (c == '\n') {
  153. buf[n++] = (char)c;
  154. }
  155. return n;
  156. }
  157. return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len TSRMLS_CC);
  158. } /* }}} */
  159. ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len TSRMLS_DC) /* {{{ */
  160. {
  161. size_t size;
  162. zend_stream_type old_type;
  163. if (file_handle->type == ZEND_HANDLE_FILENAME) {
  164. if (zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) {
  165. return FAILURE;
  166. }
  167. }
  168. switch (file_handle->type) {
  169. case ZEND_HANDLE_FD:
  170. file_handle->type = ZEND_HANDLE_FP;
  171. file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb");
  172. /* no break; */
  173. case ZEND_HANDLE_FP:
  174. if (!file_handle->handle.fp) {
  175. return FAILURE;
  176. }
  177. memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap));
  178. file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0;
  179. file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
  180. file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
  181. file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
  182. memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
  183. /* no break; */
  184. case ZEND_HANDLE_STREAM:
  185. /* nothing to do */
  186. break;
  187. case ZEND_HANDLE_MAPPED:
  188. file_handle->handle.stream.mmap.pos = 0;
  189. *buf = file_handle->handle.stream.mmap.buf;
  190. *len = file_handle->handle.stream.mmap.len;
  191. return SUCCESS;
  192. default:
  193. return FAILURE;
  194. }
  195. size = zend_stream_fsize(file_handle TSRMLS_CC);
  196. if (size == (size_t)-1) {
  197. return FAILURE;
  198. }
  199. old_type = file_handle->type;
  200. file_handle->type = ZEND_HANDLE_STREAM; /* we might still be _FP but we need fsize() work */
  201. if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) {
  202. #if HAVE_MMAP
  203. size_t page_size = REAL_PAGE_SIZE;
  204. if (file_handle->handle.fp &&
  205. size != 0 &&
  206. ((size - 1) % page_size) <= page_size - ZEND_MMAP_AHEAD) {
  207. /* *buf[size] is zeroed automatically by the kernel */
  208. *buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0);
  209. if (*buf != MAP_FAILED) {
  210. long offset = ftell(file_handle->handle.fp);
  211. file_handle->handle.stream.mmap.map = *buf;
  212. if (offset != -1) {
  213. *buf += offset;
  214. size -= offset;
  215. }
  216. file_handle->handle.stream.mmap.buf = *buf;
  217. file_handle->handle.stream.mmap.len = size;
  218. goto return_mapped;
  219. }
  220. }
  221. #endif
  222. file_handle->handle.stream.mmap.map = 0;
  223. file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD);
  224. file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size TSRMLS_CC);
  225. } else {
  226. size_t read, remain = 4*1024;
  227. *buf = emalloc(remain);
  228. size = 0;
  229. while ((read = zend_stream_read(file_handle, *buf + size, remain TSRMLS_CC)) > 0) {
  230. size += read;
  231. remain -= read;
  232. if (remain == 0) {
  233. *buf = safe_erealloc(*buf, size, 2, 0);
  234. remain = size;
  235. }
  236. }
  237. file_handle->handle.stream.mmap.map = 0;
  238. file_handle->handle.stream.mmap.len = size;
  239. if (size && remain < ZEND_MMAP_AHEAD) {
  240. *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
  241. }
  242. file_handle->handle.stream.mmap.buf = *buf;
  243. }
  244. if (file_handle->handle.stream.mmap.len == 0) {
  245. *buf = erealloc(*buf, ZEND_MMAP_AHEAD);
  246. file_handle->handle.stream.mmap.buf = *buf;
  247. }
  248. if (ZEND_MMAP_AHEAD) {
  249. memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD);
  250. }
  251. #if HAVE_MMAP
  252. return_mapped:
  253. #endif
  254. file_handle->type = ZEND_HANDLE_MAPPED;
  255. file_handle->handle.stream.mmap.pos = 0;
  256. file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle;
  257. file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer;
  258. file_handle->handle.stream.handle = &file_handle->handle.stream;
  259. file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_mmap_closer;
  260. *buf = file_handle->handle.stream.mmap.buf;
  261. *len = file_handle->handle.stream.mmap.len;
  262. return SUCCESS;
  263. } /* }}} */
  264. ZEND_API void zend_file_handle_dtor(zend_file_handle *fh TSRMLS_DC) /* {{{ */
  265. {
  266. switch (fh->type) {
  267. case ZEND_HANDLE_FD:
  268. /* nothing to do */
  269. break;
  270. case ZEND_HANDLE_FP:
  271. fclose(fh->handle.fp);
  272. break;
  273. case ZEND_HANDLE_STREAM:
  274. case ZEND_HANDLE_MAPPED:
  275. if (fh->handle.stream.closer && fh->handle.stream.handle) {
  276. fh->handle.stream.closer(fh->handle.stream.handle TSRMLS_CC);
  277. }
  278. fh->handle.stream.handle = NULL;
  279. break;
  280. case ZEND_HANDLE_FILENAME:
  281. /* We're only supposed to get here when destructing the used_files hash,
  282. * which doesn't really contain open files, but references to their names/paths
  283. */
  284. break;
  285. }
  286. if (fh->opened_path) {
  287. efree(fh->opened_path);
  288. fh->opened_path = NULL;
  289. }
  290. if (fh->free_filename && fh->filename) {
  291. efree((char*)fh->filename);
  292. fh->filename = NULL;
  293. }
  294. }
  295. /* }}} */
  296. ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
  297. {
  298. if (fh1->type != fh2->type) {
  299. return 0;
  300. }
  301. switch (fh1->type) {
  302. case ZEND_HANDLE_FD:
  303. return fh1->handle.fd == fh2->handle.fd;
  304. case ZEND_HANDLE_FP:
  305. return fh1->handle.fp == fh2->handle.fp;
  306. case ZEND_HANDLE_STREAM:
  307. return fh1->handle.stream.handle == fh2->handle.stream.handle;
  308. case ZEND_HANDLE_MAPPED:
  309. return (fh1->handle.stream.handle == &fh1->handle.stream &&
  310. fh2->handle.stream.handle == &fh2->handle.stream &&
  311. fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle)
  312. || fh1->handle.stream.handle == fh2->handle.stream.handle;
  313. default:
  314. return 0;
  315. }
  316. return 0;
  317. } /* }}} */