Coverage Report

Created: 2025-06-13 06:43

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