Back to index

php5  5.3.10
zend_stream.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | Zend Engine                                                          |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.00 of the Zend license,     |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.zend.com/license/2_00.txt.                                |
00011    | If you did not receive a copy of the Zend license and are unable to  |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@zend.com so we can mail you a copy immediately.              |
00014    +----------------------------------------------------------------------+
00015    | Authors: Wez Furlong <wez@thebrainroom.com>                          |
00016    |          Scott MacVicar <scottmac@php.net>                           |
00017    |          Nuno Lopes <nlopess@php.net>                                |
00018    |          Marcus Boerger <helly@php.net>                              |
00019    +----------------------------------------------------------------------+
00020 */
00021 
00022 /* $Id: zend_stream.c 321634 2012-01-01 13:15:04Z felipe $ */
00023 
00024 
00025 #include "zend.h"
00026 #include "zend_compile.h"
00027 
00028 #include <sys/types.h>
00029 #include <sys/stat.h>
00030 #if HAVE_MMAP
00031 # if HAVE_UNISTD_H
00032 #  include <unistd.h>
00033 #  if defined(_SC_PAGESIZE)
00034 #    define REAL_PAGE_SIZE sysconf(_SC_PAGESIZE);
00035 #  elif defined(_SC_PAGE_SIZE)
00036 #    define REAL_PAGE_SIZE sysconf(_SC_PAGE_SIZE);
00037 #  endif
00038 # endif
00039 # if HAVE_SYS_MMAN_H
00040 #  include <sys/mman.h>
00041 # endif
00042 # ifndef REAL_PAGE_SIZE
00043 #  ifdef PAGE_SIZE
00044 #   define REAL_PAGE_SIZE PAGE_SIZE
00045 #  else
00046 #   define REAL_PAGE_SIZE 4096
00047 #  endif
00048 # endif
00049 #endif
00050 
00051 ZEND_DLIMPORT int isatty(int fd);
00052 
00053 static size_t zend_stream_stdio_reader(void *handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
00054 {
00055        return fread(buf, 1, len, (FILE*)handle);
00056 } /* }}} */
00057 
00058 static void zend_stream_stdio_closer(void *handle TSRMLS_DC) /* {{{ */
00059 {
00060        if (handle && (FILE*)handle != stdin) {
00061               fclose((FILE*)handle);
00062        }
00063 } /* }}} */
00064 
00065 static size_t zend_stream_stdio_fsizer(void *handle TSRMLS_DC) /* {{{ */
00066 {
00067        struct stat buf;
00068        if (handle && fstat(fileno((FILE*)handle), &buf) == 0) {
00069 #ifdef S_ISREG
00070               if (!S_ISREG(buf.st_mode)) {
00071                      return 0;
00072               }
00073 #endif
00074               return buf.st_size;
00075        }
00076        return 0;
00077 } /* }}} */
00078 
00079 static void zend_stream_unmap(zend_stream *stream TSRMLS_DC) { /* {{{ */
00080 #if HAVE_MMAP
00081        if (stream->mmap.map) {
00082               munmap(stream->mmap.map, stream->mmap.len);
00083        } else
00084 #endif
00085        if (stream->mmap.buf) {
00086               efree(stream->mmap.buf);
00087        }
00088        stream->mmap.len = 0;
00089        stream->mmap.pos = 0;
00090        stream->mmap.map = 0;
00091        stream->mmap.buf = 0;
00092        stream->handle   = stream->mmap.old_handle;
00093 } /* }}} */
00094 
00095 static void zend_stream_mmap_closer(zend_stream *stream TSRMLS_DC) /* {{{ */
00096 {
00097        zend_stream_unmap(stream TSRMLS_CC);
00098        if (stream->mmap.old_closer && stream->handle) {
00099               stream->mmap.old_closer(stream->handle TSRMLS_CC);
00100        }
00101 } /* }}} */
00102 
00103 static inline int zend_stream_is_mmap(zend_file_handle *file_handle) { /* {{{ */
00104        return file_handle->type == ZEND_HANDLE_MAPPED;
00105 } /* }}} */
00106 
00107 static size_t zend_stream_fsize(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */
00108 {
00109        struct stat buf;
00110 
00111        if (zend_stream_is_mmap(file_handle)) {
00112               return file_handle->handle.stream.mmap.len;
00113        }
00114        if (file_handle->type == ZEND_HANDLE_STREAM || file_handle->type == ZEND_HANDLE_MAPPED) {
00115               return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle TSRMLS_CC);
00116        }
00117        if (file_handle->handle.fp && fstat(fileno(file_handle->handle.fp), &buf) == 0) {
00118 #ifdef S_ISREG
00119               if (!S_ISREG(buf.st_mode)) {
00120                      return 0;
00121               }
00122 #endif
00123               return buf.st_size;
00124        }
00125 
00126        return -1;
00127 } /* }}} */
00128 
00129 ZEND_API int zend_stream_open(const char *filename, zend_file_handle *handle TSRMLS_DC) /* {{{ */
00130 {
00131        if (zend_stream_open_function) {
00132               return zend_stream_open_function(filename, handle TSRMLS_CC);
00133        }
00134        handle->type = ZEND_HANDLE_FP;
00135        handle->opened_path = NULL;
00136        handle->handle.fp = zend_fopen(filename, &handle->opened_path TSRMLS_CC);
00137        handle->filename = (char *)filename;
00138        handle->free_filename = 0;
00139        memset(&handle->handle.stream.mmap, 0, sizeof(zend_mmap));
00140        
00141        return (handle->handle.fp) ? SUCCESS : FAILURE;
00142 } /* }}} */
00143 
00144 static int zend_stream_getc(zend_file_handle *file_handle TSRMLS_DC) /* {{{ */
00145 {
00146        char buf;
00147 
00148        if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf) TSRMLS_CC)) {
00149               return (int)buf;
00150        }
00151        return EOF;
00152 } /* }}} */
00153 
00154 static size_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len TSRMLS_DC) /* {{{ */
00155 {
00156        if (!zend_stream_is_mmap(file_handle) && file_handle->handle.stream.isatty) {
00157               int c = '*';
00158               size_t n;
00159 
00160 #ifdef NETWARE
00161               /*
00162                      c != 4 check is there as fread of a character in NetWare LibC gives 4 upon ^D character.
00163                      Ascii value 4 is actually EOT character which is not defined anywhere in the LibC
00164                      or else we can use instead of hardcoded 4.
00165               */
00166               for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != 4 && c != '\n'; ++n) {
00167 #else
00168               for (n = 0; n < len && (c = zend_stream_getc(file_handle TSRMLS_CC)) != EOF && c != '\n'; ++n)  {
00169 #endif
00170                      buf[n] = (char)c;
00171               }
00172               if (c == '\n') {
00173                      buf[n++] = (char)c; 
00174               }
00175 
00176               return n;
00177        }
00178        return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len TSRMLS_CC);
00179 } /* }}} */
00180 
00181 ZEND_API int zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len TSRMLS_DC) /* {{{ */
00182 {
00183        size_t size;
00184        zend_stream_type old_type;
00185 
00186        if (file_handle->type == ZEND_HANDLE_FILENAME) {
00187               if (zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) {
00188                      return FAILURE;
00189               }
00190        }
00191 
00192        switch (file_handle->type) {              
00193               case ZEND_HANDLE_FD:
00194                      file_handle->type = ZEND_HANDLE_FP;
00195                      file_handle->handle.fp = fdopen(file_handle->handle.fd, "rb");
00196                      /* no break; */                    
00197               case ZEND_HANDLE_FP:
00198                      if (!file_handle->handle.fp) {
00199                             return FAILURE;
00200                      }
00201                      memset(&file_handle->handle.stream.mmap, 0, sizeof(zend_mmap));
00202                      file_handle->handle.stream.isatty     = isatty(fileno((FILE *)file_handle->handle.stream.handle)) ? 1 : 0;
00203                      file_handle->handle.stream.reader     = (zend_stream_reader_t)zend_stream_stdio_reader;
00204                      file_handle->handle.stream.closer     = (zend_stream_closer_t)zend_stream_stdio_closer;
00205                      file_handle->handle.stream.fsizer     = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
00206                      memset(&file_handle->handle.stream.mmap, 0, sizeof(file_handle->handle.stream.mmap));
00207                      /* no break; */                    
00208               case ZEND_HANDLE_STREAM:
00209                      /* nothing to do */
00210                      break;
00211               
00212               case ZEND_HANDLE_MAPPED:
00213                      file_handle->handle.stream.mmap.pos = 0;
00214                      *buf = file_handle->handle.stream.mmap.buf;
00215                      *len = file_handle->handle.stream.mmap.len;
00216                      return SUCCESS;
00217                      
00218               default:
00219                      return FAILURE;
00220        }
00221 
00222        size = zend_stream_fsize(file_handle TSRMLS_CC);
00223        if (size == (size_t)-1) {
00224               return FAILURE;
00225        }
00226 
00227        old_type = file_handle->type;
00228        file_handle->type = ZEND_HANDLE_STREAM;  /* we might still be _FP but we need fsize() work */
00229 
00230        if (old_type == ZEND_HANDLE_FP && !file_handle->handle.stream.isatty && size) {
00231 #if HAVE_MMAP
00232               size_t page_size = REAL_PAGE_SIZE;
00233 
00234               if (file_handle->handle.fp &&
00235                   size != 0 &&
00236                   ((size - 1) % page_size) <= page_size - ZEND_MMAP_AHEAD) {
00237                      /*  *buf[size] is zeroed automatically by the kernel */
00238                      *buf = mmap(0, size + ZEND_MMAP_AHEAD, PROT_READ, MAP_PRIVATE, fileno(file_handle->handle.fp), 0);
00239                      if (*buf != MAP_FAILED) {
00240                             long offset = ftell(file_handle->handle.fp);
00241                             file_handle->handle.stream.mmap.map = *buf;
00242 
00243                             if (offset != -1) {
00244                                    *buf += offset;
00245                                    size -= offset;
00246                             }
00247                             file_handle->handle.stream.mmap.buf = *buf;
00248                             file_handle->handle.stream.mmap.len = size;
00249 
00250                             goto return_mapped;
00251                      }
00252               }
00253 #endif
00254               file_handle->handle.stream.mmap.map = 0;
00255               file_handle->handle.stream.mmap.buf = *buf = safe_emalloc(1, size, ZEND_MMAP_AHEAD);
00256               file_handle->handle.stream.mmap.len = zend_stream_read(file_handle, *buf, size TSRMLS_CC);
00257        } else {
00258               size_t read, remain = 4*1024;
00259               *buf = emalloc(remain);
00260               size = 0;
00261 
00262               while ((read = zend_stream_read(file_handle, *buf + size, remain TSRMLS_CC)) > 0) {
00263                      size   += read;
00264                      remain -= read;
00265 
00266                      if (remain == 0) {
00267                             *buf   = safe_erealloc(*buf, size, 2, 0);
00268                             remain = size;
00269                      }
00270               }
00271               file_handle->handle.stream.mmap.map = 0;
00272               file_handle->handle.stream.mmap.len = size;
00273               if (size && remain < ZEND_MMAP_AHEAD) {
00274                      *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
00275               }
00276               file_handle->handle.stream.mmap.buf = *buf;
00277        }
00278 
00279        if (file_handle->handle.stream.mmap.len == 0) {
00280               *buf = erealloc(*buf, ZEND_MMAP_AHEAD);
00281               file_handle->handle.stream.mmap.buf = *buf;
00282        }
00283 
00284        if (ZEND_MMAP_AHEAD) {
00285               memset(file_handle->handle.stream.mmap.buf + file_handle->handle.stream.mmap.len, 0, ZEND_MMAP_AHEAD);
00286        }
00287 
00288 #if HAVE_MMAP
00289 return_mapped:
00290 #endif
00291        file_handle->type = ZEND_HANDLE_MAPPED;
00292        file_handle->handle.stream.mmap.pos        = 0;
00293        file_handle->handle.stream.mmap.old_handle = file_handle->handle.stream.handle;
00294        file_handle->handle.stream.mmap.old_closer = file_handle->handle.stream.closer;
00295        file_handle->handle.stream.handle          = &file_handle->handle.stream;
00296        file_handle->handle.stream.closer          = (zend_stream_closer_t)zend_stream_mmap_closer;
00297 
00298        *buf = file_handle->handle.stream.mmap.buf;
00299        *len = file_handle->handle.stream.mmap.len;
00300 
00301        return SUCCESS;
00302 } /* }}} */
00303 
00304 ZEND_API void zend_file_handle_dtor(zend_file_handle *fh TSRMLS_DC) /* {{{ */
00305 {
00306        switch (fh->type) {
00307               case ZEND_HANDLE_FD:
00308                      /* nothing to do */
00309                      break;
00310               case ZEND_HANDLE_FP:
00311                      fclose(fh->handle.fp);
00312                      break;
00313               case ZEND_HANDLE_STREAM:
00314               case ZEND_HANDLE_MAPPED:
00315                      if (fh->handle.stream.closer && fh->handle.stream.handle) {
00316                             fh->handle.stream.closer(fh->handle.stream.handle TSRMLS_CC);
00317                      }
00318                      fh->handle.stream.handle = NULL;
00319                      break;
00320               case ZEND_HANDLE_FILENAME:
00321                      /* We're only supposed to get here when destructing the used_files hash,
00322                       * which doesn't really contain open files, but references to their names/paths
00323                       */
00324                      break;
00325        }
00326        if (fh->opened_path) {
00327               efree(fh->opened_path);
00328               fh->opened_path = NULL;
00329        }
00330        if (fh->free_filename && fh->filename) {
00331               efree(fh->filename);
00332               fh->filename = NULL;
00333        }
00334 }
00335 /* }}} */
00336 
00337 ZEND_API int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
00338 {
00339        if (fh1->type != fh2->type) {
00340               return 0;
00341        }
00342        switch (fh1->type) {
00343               case ZEND_HANDLE_FD:
00344                      return fh1->handle.fd == fh2->handle.fd;
00345               case ZEND_HANDLE_FP:
00346                      return fh1->handle.fp == fh2->handle.fp;
00347               case ZEND_HANDLE_STREAM:
00348                      return fh1->handle.stream.handle == fh2->handle.stream.handle;
00349               case ZEND_HANDLE_MAPPED:
00350                      return (fh1->handle.stream.handle == &fh1->handle.stream &&
00351                              fh2->handle.stream.handle == &fh2->handle.stream &&
00352                              fh1->handle.stream.mmap.old_handle == fh2->handle.stream.mmap.old_handle)
00353                             || fh1->handle.stream.handle == fh2->handle.stream.handle;
00354               default:
00355                      return 0;
00356        }
00357        return 0;
00358 } /* }}} */