Coverage Report

Created: 2025-11-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/zend_stream.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend Engine                                                          |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) 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
22
#include "zend.h"
23
#include "zend_compile.h"
24
#include "zend_stream.h"
25
26
ZEND_DLIMPORT int isatty(int fd);
27
28
static ssize_t zend_stream_stdio_reader(void *handle, char *buf, size_t len) /* {{{ */
29
0
{
30
0
  return fread(buf, 1, len, (FILE*)handle);
31
0
} /* }}} */
32
33
static void zend_stream_stdio_closer(void *handle) /* {{{ */
34
0
{
35
0
  if (handle && (FILE*)handle != stdin) {
36
0
    fclose((FILE*)handle);
37
0
  }
38
0
} /* }}} */
39
40
static size_t zend_stream_stdio_fsizer(void *handle) /* {{{ */
41
0
{
42
0
  zend_stat_t buf = {0};
43
0
  if (handle && zend_fstat(fileno((FILE*)handle), &buf) == 0) {
44
0
#ifdef S_ISREG
45
0
    if (!S_ISREG(buf.st_mode)) {
46
0
      return 0;
47
0
    }
48
0
#endif
49
0
    return buf.st_size;
50
0
  }
51
0
  return -1;
52
0
} /* }}} */
53
54
static size_t zend_stream_fsize(zend_file_handle *file_handle) /* {{{ */
55
109
{
56
109
  ZEND_ASSERT(file_handle->type == ZEND_HANDLE_STREAM);
57
109
  if (file_handle->handle.stream.isatty) {
58
0
    return 0;
59
0
  }
60
109
  return file_handle->handle.stream.fsizer(file_handle->handle.stream.handle);
61
109
} /* }}} */
62
63
0
ZEND_API void zend_stream_init_fp(zend_file_handle *handle, FILE *fp, const char *filename) {
64
0
  memset(handle, 0, sizeof(zend_file_handle));
65
0
  handle->type = ZEND_HANDLE_FP;
66
0
  handle->handle.fp = fp;
67
0
  handle->filename = filename ? zend_string_init(filename, strlen(filename), 0) : NULL;
68
0
}
69
70
160k
ZEND_API void zend_stream_init_filename(zend_file_handle *handle, const char *filename) {
71
160k
  memset(handle, 0, sizeof(zend_file_handle));
72
160k
  handle->type = ZEND_HANDLE_FILENAME;
73
160k
  handle->filename = filename ? zend_string_init(filename, strlen(filename), 0) : NULL;
74
160k
}
75
76
150k
ZEND_API void zend_stream_init_filename_ex(zend_file_handle *handle, zend_string *filename) {
77
150k
  memset(handle, 0, sizeof(zend_file_handle));
78
150k
  handle->type = ZEND_HANDLE_FILENAME;
79
150k
  handle->filename = zend_string_copy(filename);
80
150k
}
81
82
ZEND_API zend_result zend_stream_open(zend_file_handle *handle) /* {{{ */
83
1.60k
{
84
1.60k
  zend_string *opened_path;
85
86
1.60k
  ZEND_ASSERT(handle->type == ZEND_HANDLE_FILENAME);
87
1.60k
  if (zend_stream_open_function) {
88
1.60k
    return zend_stream_open_function(handle);
89
1.60k
  }
90
91
0
  handle->handle.fp = zend_fopen(handle->filename, &opened_path);
92
0
  if (!handle->handle.fp) {
93
0
    return FAILURE;
94
0
  }
95
0
  handle->type = ZEND_HANDLE_FP;
96
0
  return SUCCESS;
97
0
} /* }}} */
98
99
static int zend_stream_getc(zend_file_handle *file_handle) /* {{{ */
100
0
{
101
0
  char buf;
102
103
0
  if (file_handle->handle.stream.reader(file_handle->handle.stream.handle, &buf, sizeof(buf))) {
104
0
    return (int)buf;
105
0
  }
106
0
  return EOF;
107
0
} /* }}} */
108
109
static ssize_t zend_stream_read(zend_file_handle *file_handle, char *buf, size_t len) /* {{{ */
110
122
{
111
122
  if (file_handle->handle.stream.isatty) {
112
0
    int c = '*';
113
0
    size_t n;
114
115
0
    for (n = 0; n < len && (c = zend_stream_getc(file_handle)) != EOF && c != '\n'; ++n)  {
116
0
      buf[n] = (char)c;
117
0
    }
118
0
    if (c == '\n') {
119
0
      buf[n++] = (char)c;
120
0
    }
121
122
0
    return n;
123
0
  }
124
122
  return file_handle->handle.stream.reader(file_handle->handle.stream.handle, buf, len);
125
122
} /* }}} */
126
127
ZEND_API zend_result zend_stream_fixup(zend_file_handle *file_handle, char **buf, size_t *len) /* {{{ */
128
124k
{
129
124k
  size_t file_size;
130
131
124k
  if (file_handle->buf) {
132
123k
    *buf = file_handle->buf;
133
123k
    *len = file_handle->len;
134
123k
    return SUCCESS;
135
123k
  }
136
137
1.46k
  if (file_handle->type == ZEND_HANDLE_FILENAME) {
138
1.46k
    if (zend_stream_open(file_handle) == FAILURE) {
139
696
      return FAILURE;
140
696
    }
141
1.46k
  }
142
143
773
  if (file_handle->type == ZEND_HANDLE_FP) {
144
0
    if (!file_handle->handle.fp) {
145
0
      return FAILURE;
146
0
    }
147
148
0
    file_handle->type = ZEND_HANDLE_STREAM;
149
0
    file_handle->handle.stream.handle = file_handle->handle.fp;
150
0
    file_handle->handle.stream.isatty = isatty(fileno((FILE *)file_handle->handle.stream.handle));
151
0
    file_handle->handle.stream.reader = (zend_stream_reader_t)zend_stream_stdio_reader;
152
0
    file_handle->handle.stream.closer = (zend_stream_closer_t)zend_stream_stdio_closer;
153
0
    file_handle->handle.stream.fsizer = (zend_stream_fsizer_t)zend_stream_stdio_fsizer;
154
0
  }
155
156
773
  file_size = zend_stream_fsize(file_handle);
157
773
  if (file_size == (size_t)-1) {
158
0
    return FAILURE;
159
0
  }
160
161
773
  if (file_size) {
162
18
    ssize_t read;
163
18
    size_t size = 0;
164
18
    *buf = safe_emalloc(1, file_size, ZEND_MMAP_AHEAD);
165
27
    while ((read = zend_stream_read(file_handle, *buf + size, file_size - size)) > 0) {
166
9
      size += read;
167
9
    }
168
18
    if (read < 0) {
169
5
      efree(*buf);
170
5
      return FAILURE;
171
5
    }
172
13
    file_handle->buf = *buf;
173
13
    file_handle->len = size;
174
755
  } else {
175
755
    size_t size = 0, remain = 4*1024;
176
755
    ssize_t read;
177
755
    *buf = emalloc(remain);
178
179
759
    while ((read = zend_stream_read(file_handle, *buf + size, remain)) > 0) {
180
4
      size   += read;
181
4
      remain -= read;
182
183
4
      if (remain == 0) {
184
0
        *buf   = safe_erealloc(*buf, size, 2, 0);
185
0
        remain = size;
186
0
      }
187
4
    }
188
755
    if (read < 0) {
189
26
      efree(*buf);
190
26
      return FAILURE;
191
26
    }
192
193
729
    file_handle->len = size;
194
729
    if (size && remain < ZEND_MMAP_AHEAD) {
195
0
      *buf = safe_erealloc(*buf, size, 1, ZEND_MMAP_AHEAD);
196
0
    }
197
729
    file_handle->buf = *buf;
198
729
  }
199
200
742
  if (file_handle->len == 0) {
201
50
    *buf = erealloc(*buf, ZEND_MMAP_AHEAD);
202
50
    file_handle->buf = *buf;
203
50
  }
204
205
742
  memset(file_handle->buf + file_handle->len, 0, ZEND_MMAP_AHEAD);
206
207
742
  *buf = file_handle->buf;
208
742
  *len = file_handle->len;
209
210
742
  return SUCCESS;
211
773
} /* }}} */
212
213
static void zend_file_handle_dtor(zend_file_handle *fh) /* {{{ */
214
307k
{
215
307k
  switch (fh->type) {
216
0
    case ZEND_HANDLE_FP:
217
0
      if (fh->handle.fp) {
218
0
        fclose(fh->handle.fp);
219
0
        fh->handle.fp = NULL;
220
0
      }
221
0
      break;
222
160k
    case ZEND_HANDLE_STREAM:
223
160k
      if (fh->handle.stream.closer && fh->handle.stream.handle) {
224
94
        fh->handle.stream.closer(fh->handle.stream.handle);
225
94
      }
226
160k
      fh->handle.stream.handle = NULL;
227
160k
      break;
228
147k
    case ZEND_HANDLE_FILENAME:
229
      /* We're only supposed to get here when destructing the used_files hash,
230
       * which doesn't really contain open files, but references to their names/paths
231
       */
232
147k
      break;
233
307k
  }
234
307k
  if (fh->opened_path) {
235
71
    zend_string_release_ex(fh->opened_path, 0);
236
71
    fh->opened_path = NULL;
237
71
  }
238
307k
  if (fh->buf) {
239
160k
    efree(fh->buf);
240
160k
    fh->buf = NULL;
241
160k
  }
242
307k
  if (fh->filename) {
243
307k
    zend_string_release(fh->filename);
244
307k
    fh->filename = NULL;
245
307k
  }
246
307k
}
247
/* }}} */
248
249
/* return int to be compatible with Zend linked list API */
250
static int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) /* {{{ */
251
117k
{
252
117k
  if (fh1->type != fh2->type) {
253
0
    return 0;
254
0
  }
255
117k
  switch (fh1->type) {
256
690
    case ZEND_HANDLE_FILENAME:
257
690
      return zend_string_equals(fh1->filename, fh2->filename);
258
0
    case ZEND_HANDLE_FP:
259
0
      return fh1->handle.fp == fh2->handle.fp;
260
117k
    case ZEND_HANDLE_STREAM:
261
117k
      return fh1->handle.stream.handle == fh2->handle.stream.handle;
262
0
    default:
263
0
      return 0;
264
117k
  }
265
0
  return 0;
266
117k
} /* }}} */
267
268
ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle) /* {{{ */
269
301k
{
270
301k
  if (file_handle->in_list) {
271
117k
    zend_llist_del_element(&CG(open_files), file_handle, (int (*)(void *, void *)) zend_compare_file_handles);
272
    /* zend_file_handle_dtor() operates on the copy, so we have to NULLify the original here */
273
117k
    file_handle->opened_path = NULL;
274
117k
    file_handle->filename = NULL;
275
183k
  } else {
276
183k
    zend_file_handle_dtor(file_handle);
277
183k
  }
278
301k
} /* }}} */
279
280
void zend_stream_init(void) /* {{{ */
281
247k
{
282
247k
  zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) zend_file_handle_dtor, 0);
283
247k
} /* }}} */
284
285
void zend_stream_shutdown(void) /* {{{ */
286
247k
{
287
247k
  zend_llist_destroy(&CG(open_files));
288
247k
} /* }}} */