xref: /PHP-8.4/main/streams/glob_wrapper.c (revision 18d70db0)
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 #ifdef HAVE_GLOB
21 # ifndef PHP_WIN32
22 #  include <glob.h>
23 # else
24 #  include "win32/glob.h"
25 # endif
26 #endif
27 
28 #ifdef HAVE_GLOB
29 #ifndef GLOB_ONLYDIR
30 #define GLOB_ONLYDIR (1<<30)
31 #define GLOB_FLAGMASK (~GLOB_ONLYDIR)
32 #else
33 #define GLOB_FLAGMASK (~0)
34 #endif
35 
36 typedef struct {
37 	glob_t   glob;
38 	size_t   index;
39 	int      flags;
40 	char     *path;
41 	size_t   path_len;
42 	char     *pattern;
43 	size_t   pattern_len;
44 	size_t   *open_basedir_indexmap;
45 	size_t   open_basedir_indexmap_size;
46 	bool     open_basedir_used;
47 } glob_s_t;
48 
_php_glob_stream_get_path(php_stream * stream,size_t * plen STREAMS_DC)49 PHPAPI char* _php_glob_stream_get_path(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
50 {
51 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
52 
53 	if (pglob && pglob->path) {
54 		if (plen) {
55 			*plen = pglob->path_len;
56 		}
57 		return pglob->path;
58 	} else {
59 		if (plen) {
60 			*plen = 0;
61 		}
62 		return NULL;
63 	}
64 }
65 /* }}} */
66 
_php_glob_stream_get_pattern(php_stream * stream,size_t * plen STREAMS_DC)67 PHPAPI char* _php_glob_stream_get_pattern(php_stream *stream, size_t *plen STREAMS_DC) /* {{{ */
68 {
69 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
70 
71 	if (pglob && pglob->pattern) {
72 		if (plen) {
73 			*plen = pglob->pattern_len;
74 		}
75 		return pglob->pattern;
76 	} else {
77 		if (plen) {
78 			*plen = 0;
79 		}
80 		return NULL;
81 	}
82 }
83 /* }}} */
84 
php_glob_stream_get_result_count(glob_s_t * pglob)85 static inline int php_glob_stream_get_result_count(glob_s_t *pglob)
86 {
87 	return pglob->open_basedir_used ? (int) pglob->open_basedir_indexmap_size : pglob->glob.gl_pathc;
88 }
89 
_php_glob_stream_get_count(php_stream * stream,int * pflags STREAMS_DC)90 PHPAPI int _php_glob_stream_get_count(php_stream *stream, int *pflags STREAMS_DC) /* {{{ */
91 {
92 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
93 
94 	if (pglob) {
95 		if (pflags) {
96 			*pflags = pglob->flags;
97 		}
98 		return php_glob_stream_get_result_count(pglob);
99 	} else {
100 		if (pflags) {
101 			*pflags = 0;
102 		}
103 		return 0;
104 	}
105 }
106 /* }}} */
107 
php_glob_stream_path_split(glob_s_t * pglob,const char * path,int get_path,const char ** p_file)108 static void php_glob_stream_path_split(glob_s_t *pglob, const char *path, int get_path, const char **p_file) /* {{{ */
109 {
110 	const char *pos, *gpath = path;
111 
112 	if ((pos = strrchr(path, '/')) != NULL) {
113 		path = pos+1;
114 	}
115 #ifdef PHP_WIN32
116 	if ((pos = strrchr(path, '\\')) != NULL) {
117 		path = pos+1;
118 	}
119 #endif
120 
121 	*p_file = path;
122 
123 	if (get_path) {
124 		if (pglob->path) {
125 			efree(pglob->path);
126 		}
127 		if ((path - gpath) > 1) {
128 			path--;
129 		}
130 		pglob->path_len = path - gpath;
131 		pglob->path = estrndup(gpath, pglob->path_len);
132 	}
133 }
134 /* }}} */
135 
php_glob_stream_read(php_stream * stream,char * buf,size_t count)136 static ssize_t php_glob_stream_read(php_stream *stream, char *buf, size_t count) /* {{{ */
137 {
138 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
139 	php_stream_dirent *ent = (php_stream_dirent*)buf;
140 	const char *path;
141 	int glob_result_count;
142 	size_t index;
143 
144 	/* avoid problems if someone mis-uses the stream */
145 	if (count == sizeof(php_stream_dirent) && pglob) {
146 		glob_result_count = php_glob_stream_get_result_count(pglob);
147 		if (pglob->index < (size_t) glob_result_count) {
148 			index = pglob->open_basedir_used && pglob->open_basedir_indexmap ?
149 					pglob->open_basedir_indexmap[pglob->index] : pglob->index;
150 			php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[index], pglob->flags & GLOB_APPEND, &path);
151 			++pglob->index;
152 			PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path));
153 			ent->d_type = DT_UNKNOWN;
154 			return sizeof(php_stream_dirent);
155 		}
156 		pglob->index = glob_result_count;
157 		if (pglob->path) {
158 			efree(pglob->path);
159 			pglob->path = NULL;
160 		}
161 	}
162 
163 	return -1;
164 }
165 /* }}} */
166 
php_glob_stream_close(php_stream * stream,int close_handle)167 static int php_glob_stream_close(php_stream *stream, int close_handle)  /* {{{ */
168 {
169 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
170 
171 	if (pglob) {
172 		pglob->index = 0;
173 		globfree(&pglob->glob);
174 		if (pglob->path) {
175 			efree(pglob->path);
176 		}
177 		if (pglob->pattern) {
178 			efree(pglob->pattern);
179 		}
180 		if (pglob->open_basedir_indexmap) {
181 			efree(pglob->open_basedir_indexmap);
182 		}
183 	}
184 	efree(stream->abstract);
185 	return 0;
186 }
187 /* {{{ */
188 
php_glob_stream_rewind(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)189 static int php_glob_stream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) /* {{{ */
190 {
191 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
192 
193 	if (pglob) {
194 		pglob->index = 0;
195 		if (pglob->path) {
196 			efree(pglob->path);
197 			pglob->path = NULL;
198 		}
199 	}
200 	return 0;
201 }
202 /* }}} */
203 
204 const php_stream_ops  php_glob_stream_ops = {
205 	NULL, php_glob_stream_read,
206 	php_glob_stream_close, NULL,
207 	"glob",
208 	php_glob_stream_rewind,
209 	NULL, /* cast */
210 	NULL, /* stat */
211 	NULL  /* set_option */
212 };
213 
214  /* {{{ php_glob_stream_opener */
php_glob_stream_opener(php_stream_wrapper * wrapper,const char * path,const char * mode,int options,zend_string ** opened_path,php_stream_context * context STREAMS_DC)215 static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
216 		int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
217 {
218 	glob_s_t *pglob;
219 	int ret, i;
220 	const char *tmp, *pos;
221 
222 	if (!strncmp(path, "glob://", sizeof("glob://")-1)) {
223 		path += sizeof("glob://")-1;
224 		if (opened_path) {
225 			*opened_path = zend_string_init(path, strlen(path), 0);
226 		}
227 	}
228 
229 	pglob = ecalloc(1, sizeof(*pglob));
230 
231 	if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) {
232 #ifdef GLOB_NOMATCH
233 		if (GLOB_NOMATCH != ret)
234 #endif
235 		{
236 			efree(pglob);
237 			return NULL;
238 		}
239 	}
240 
241 	/* if open_basedir in use, check and filter restricted paths */
242 	if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) {
243 		pglob->open_basedir_used = true;
244 		for (i = 0; i < pglob->glob.gl_pathc; i++) {
245 			if (!php_check_open_basedir_ex(pglob->glob.gl_pathv[i], 0)) {
246 				if (!pglob->open_basedir_indexmap) {
247 					pglob->open_basedir_indexmap = (size_t *) safe_emalloc(
248 							pglob->glob.gl_pathc, sizeof(size_t), 0);
249 				}
250 				pglob->open_basedir_indexmap[pglob->open_basedir_indexmap_size++] = i;
251 			}
252 		}
253 	}
254 
255 	pos = path;
256 	if ((tmp = strrchr(pos, '/')) != NULL) {
257 		pos = tmp+1;
258 	}
259 #ifdef PHP_WIN32
260 	if ((tmp = strrchr(pos, '\\')) != NULL) {
261 		pos = tmp+1;
262 	}
263 #endif
264 
265 	pglob->pattern_len = strlen(pos);
266 	pglob->pattern = estrndup(pos, pglob->pattern_len);
267 
268 	pglob->flags |= GLOB_APPEND;
269 
270 	if (pglob->glob.gl_pathc) {
271 		php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[0], 1, &tmp);
272 	} else {
273 		php_glob_stream_path_split(pglob, path, 1, &tmp);
274 	}
275 
276 	return php_stream_alloc(&php_glob_stream_ops, pglob, 0, mode);
277 }
278 /* }}} */
279 
280 static const php_stream_wrapper_ops  php_glob_stream_wrapper_ops = {
281 	NULL,
282 	NULL,
283 	NULL,
284 	NULL,
285 	php_glob_stream_opener,
286 	"glob",
287 	NULL,
288 	NULL,
289 	NULL,
290 	NULL,
291 	NULL
292 };
293 
294 const php_stream_wrapper  php_glob_stream_wrapper = {
295 	&php_glob_stream_wrapper_ops,
296 	NULL,
297 	0
298 };
299 #endif /* HAVE_GLOB */
300