Coverage Report

Created: 2026-06-02 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/streams/glob_wrapper.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Marcus Boerger <helly@php.net>                              |
12
   +----------------------------------------------------------------------+
13
 */
14
15
#include "php.h"
16
#include "php_streams_int.h"
17
18
#include "php_glob.h"
19
20
typedef struct {
21
  php_glob_t glob;
22
  size_t   index;
23
  int      flags;
24
  char     *path;
25
  size_t   path_len;
26
  char     *pattern;
27
  size_t   pattern_len;
28
  size_t   *open_basedir_indexmap;
29
  size_t   open_basedir_indexmap_size;
30
  bool     open_basedir_used;
31
} glob_s_t;
32
33
PHPAPI char* _php_glob_stream_get_path(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
34
0
{
35
0
  glob_s_t *pglob = (glob_s_t *)stream->abstract;
36
37
0
  if (pglob && pglob->path) {
38
0
    if (plen) {
39
0
      *plen = pglob->path_len;
40
0
    }
41
0
    return pglob->path;
42
0
  } else {
43
0
    if (plen) {
44
0
      *plen = 0;
45
0
    }
46
0
    return NULL;
47
0
  }
48
0
}
49
/* }}} */
50
51
PHPAPI char* _php_glob_stream_get_pattern(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
52
0
{
53
0
  glob_s_t *pglob = (glob_s_t *)stream->abstract;
54
55
0
  if (pglob && pglob->pattern) {
56
0
    if (plen) {
57
0
      *plen = pglob->pattern_len;
58
0
    }
59
0
    return pglob->pattern;
60
0
  } else {
61
0
    if (plen) {
62
0
      *plen = 0;
63
0
    }
64
0
    return NULL;
65
0
  }
66
0
}
67
/* }}} */
68
69
static inline int php_glob_stream_get_result_count(glob_s_t *pglob)
70
0
{
71
0
  return pglob->open_basedir_used ? (int) pglob->open_basedir_indexmap_size : pglob->glob.gl_pathc;
72
0
}
73
74
PHPAPI int _php_glob_stream_get_count(php_stream *stream, int *pflags STREAMS_DC) /* {{{ */
75
0
{
76
0
  glob_s_t *pglob = (glob_s_t *)stream->abstract;
77
78
0
  if (pglob) {
79
0
    if (pflags) {
80
0
      *pflags = pglob->flags;
81
0
    }
82
0
    return php_glob_stream_get_result_count(pglob);
83
0
  } else {
84
0
    if (pflags) {
85
0
      *pflags = 0;
86
0
    }
87
0
    return 0;
88
0
  }
89
0
}
90
/* }}} */
91
92
static void php_glob_stream_path_split(glob_s_t *pglob, const char *path, int get_path, const char **p_file) /* {{{ */
93
0
{
94
0
  const char *pos, *gpath = path;
95
96
0
  if ((pos = strrchr(path, '/')) != NULL) {
97
0
    path = pos+1;
98
0
  }
99
#ifdef PHP_WIN32
100
  if ((pos = strrchr(path, '\\')) != NULL) {
101
    path = pos+1;
102
  }
103
#endif
104
105
0
  *p_file = path;
106
107
0
  if (get_path) {
108
0
    if (pglob->path) {
109
0
      efree(pglob->path);
110
0
    }
111
0
    if ((path - gpath) > 1) {
112
0
      path--;
113
0
    }
114
0
    pglob->path_len = path - gpath;
115
0
    pglob->path = estrndup(gpath, pglob->path_len);
116
0
  }
117
0
}
118
/* }}} */
119
120
static ssize_t php_glob_stream_read(php_stream *stream, char *buf, size_t count) /* {{{ */
121
0
{
122
0
  glob_s_t *pglob = (glob_s_t *)stream->abstract;
123
0
  php_stream_dirent *ent = (php_stream_dirent*)buf;
124
0
  const char *path;
125
0
  int glob_result_count;
126
0
  size_t index;
127
128
  /* avoid problems if someone mis-uses the stream */
129
0
  if (count == sizeof(php_stream_dirent) && pglob) {
130
0
    glob_result_count = php_glob_stream_get_result_count(pglob);
131
0
    if (pglob->index < (size_t) glob_result_count) {
132
0
      index = pglob->open_basedir_used && pglob->open_basedir_indexmap ?
133
0
          pglob->open_basedir_indexmap[pglob->index] : pglob->index;
134
0
      php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[index], pglob->flags & PHP_GLOB_APPEND, &path);
135
0
      ++pglob->index;
136
0
      PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path));
137
0
      ent->d_type = DT_UNKNOWN;
138
0
      return sizeof(php_stream_dirent);
139
0
    }
140
0
    pglob->index = glob_result_count;
141
0
    if (pglob->path) {
142
0
      efree(pglob->path);
143
0
      pglob->path = NULL;
144
0
    }
145
0
  }
146
147
0
  return -1;
148
0
}
149
/* }}} */
150
151
static int php_glob_stream_close(php_stream *stream, int close_handle)  /* {{{ */
152
0
{
153
0
  glob_s_t *pglob = (glob_s_t *)stream->abstract;
154
155
0
  if (pglob) {
156
0
    pglob->index = 0;
157
0
    php_globfree(&pglob->glob);
158
0
    if (pglob->path) {
159
0
      efree(pglob->path);
160
0
    }
161
0
    if (pglob->pattern) {
162
0
      efree(pglob->pattern);
163
0
    }
164
0
    if (pglob->open_basedir_indexmap) {
165
0
      efree(pglob->open_basedir_indexmap);
166
0
    }
167
0
  }
168
0
  efree(stream->abstract);
169
0
  return 0;
170
0
}
171
/* {{{ */
172
173
static int php_glob_stream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) /* {{{ */
174
0
{
175
0
  glob_s_t *pglob = (glob_s_t *)stream->abstract;
176
177
0
  if (pglob) {
178
0
    pglob->index = 0;
179
0
    if (pglob->path) {
180
0
      efree(pglob->path);
181
0
      pglob->path = NULL;
182
0
    }
183
0
  }
184
0
  return 0;
185
0
}
186
/* }}} */
187
188
const php_stream_ops  php_glob_stream_ops = {
189
  NULL, php_glob_stream_read,
190
  php_glob_stream_close, NULL,
191
  "glob",
192
  php_glob_stream_rewind,
193
  NULL, /* cast */
194
  NULL, /* stat */
195
  NULL  /* set_option */
196
};
197
198
 /* {{{ php_glob_stream_opener */
199
static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
200
    int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
201
0
{
202
0
  glob_s_t *pglob;
203
0
  int ret, i;
204
0
  const char *tmp, *pos;
205
206
0
  if (!strncmp(path, "glob://", sizeof("glob://")-1)) {
207
0
    path += sizeof("glob://")-1;
208
0
    if (opened_path) {
209
0
      *opened_path = zend_string_init(path, strlen(path), 0);
210
0
    }
211
0
  }
212
0
  const char *pattern = path;
213
#ifdef ZTS
214
  char cwd[MAXPATHLEN];
215
  char work_pattern[MAXPATHLEN];
216
  char *result;
217
  size_t cwd_skip = 0;
218
  if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
219
    result = VCWD_GETCWD(cwd, MAXPATHLEN);
220
    if (!result) {
221
      cwd[0] = '\0';
222
    }
223
# ifdef PHP_WIN32
224
    if (IS_SLASH(*path)) {
225
      cwd[2] = '\0';
226
    }
227
# endif
228
    cwd_skip = strlen(cwd)+1;
229
230
    snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path);
231
    pattern = work_pattern;
232
  }
233
#endif
234
235
0
  pglob = ecalloc(1, sizeof(*pglob));
236
237
0
  if (0 != (ret = php_glob(pattern, pglob->flags & PHP_GLOB_FLAGMASK, NULL, &pglob->glob))) {
238
0
#ifdef PHP_GLOB_NOMATCH
239
0
    if (PHP_GLOB_NOMATCH != ret)
240
0
#endif
241
0
    {
242
0
      efree(pglob);
243
0
      return NULL;
244
0
    }
245
0
  }
246
247
#ifdef ZTS
248
  if (cwd_skip > 0) {
249
    /* strip prepended CWD */
250
    for (i = 0; i < pglob->glob.gl_pathc; i++) {
251
      char *p = pglob->glob.gl_pathv[i];
252
      char *q = p + cwd_skip;
253
      char *e = p + strlen(pglob->glob.gl_pathv[i]) - 1;
254
      while (q <= e) {
255
        *p++ = *q++;
256
      }
257
      *p = '\0';
258
    }
259
  }
260
#endif
261
262
  /* if open_basedir in use, check and filter restricted paths */
263
0
  if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) {
264
0
    pglob->open_basedir_used = true;
265
0
    for (i = 0; i < pglob->glob.gl_pathc; i++) {
266
0
      if (!php_check_open_basedir_ex(pglob->glob.gl_pathv[i], 0)) {
267
0
        if (!pglob->open_basedir_indexmap) {
268
0
          pglob->open_basedir_indexmap = (size_t *) safe_emalloc(
269
0
              pglob->glob.gl_pathc, sizeof(size_t), 0);
270
0
        }
271
0
        pglob->open_basedir_indexmap[pglob->open_basedir_indexmap_size++] = i;
272
0
      }
273
0
    }
274
0
  }
275
276
0
  pos = path;
277
0
  if ((tmp = strrchr(pos, '/')) != NULL) {
278
0
    pos = tmp+1;
279
0
  }
280
#ifdef PHP_WIN32
281
  if ((tmp = strrchr(pos, '\\')) != NULL) {
282
    pos = tmp+1;
283
  }
284
#endif
285
286
0
  pglob->pattern_len = strlen(pos);
287
0
  pglob->pattern = estrndup(pos, pglob->pattern_len);
288
289
0
  pglob->flags |= PHP_GLOB_APPEND;
290
291
0
  if (pglob->glob.gl_pathc) {
292
0
    php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[0], 1, &tmp);
293
0
  } else {
294
0
    php_glob_stream_path_split(pglob, path, 1, &tmp);
295
0
  }
296
297
  return php_stream_alloc(&php_glob_stream_ops, pglob, 0, mode);
298
0
}
299
/* }}} */
300
301
static const php_stream_wrapper_ops  php_glob_stream_wrapper_ops = {
302
  NULL,
303
  NULL,
304
  NULL,
305
  NULL,
306
  php_glob_stream_opener,
307
  "glob",
308
  NULL,
309
  NULL,
310
  NULL,
311
  NULL,
312
  NULL
313
};
314
315
const php_stream_wrapper  php_glob_stream_wrapper = {
316
  &php_glob_stream_wrapper_ops,
317
  NULL,
318
  0
319
};