Coverage Report

Created: 2026-06-02 06:39

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