zend_stream.c 10 KB

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