xref: /PHP-8.2/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 			return sizeof(php_stream_dirent);
154 		}
155 		pglob->index = glob_result_count;
156 		if (pglob->path) {
157 			efree(pglob->path);
158 			pglob->path = NULL;
159 		}
160 	}
161 
162 	return -1;
163 }
164 /* }}} */
165 
php_glob_stream_close(php_stream * stream,int close_handle)166 static int php_glob_stream_close(php_stream *stream, int close_handle)  /* {{{ */
167 {
168 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
169 
170 	if (pglob) {
171 		pglob->index = 0;
172 		globfree(&pglob->glob);
173 		if (pglob->path) {
174 			efree(pglob->path);
175 		}
176 		if (pglob->pattern) {
177 			efree(pglob->pattern);
178 		}
179 		if (pglob->open_basedir_indexmap) {
180 			efree(pglob->open_basedir_indexmap);
181 		}
182 	}
183 	efree(stream->abstract);
184 	return 0;
185 }
186 /* {{{ */
187 
php_glob_stream_rewind(php_stream * stream,zend_off_t offset,int whence,zend_off_t * newoffs)188 static int php_glob_stream_rewind(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) /* {{{ */
189 {
190 	glob_s_t *pglob = (glob_s_t *)stream->abstract;
191 
192 	if (pglob) {
193 		pglob->index = 0;
194 		if (pglob->path) {
195 			efree(pglob->path);
196 			pglob->path = NULL;
197 		}
198 	}
199 	return 0;
200 }
201 /* }}} */
202 
203 const php_stream_ops  php_glob_stream_ops = {
204 	NULL, php_glob_stream_read,
205 	php_glob_stream_close, NULL,
206 	"glob",
207 	php_glob_stream_rewind,
208 	NULL, /* cast */
209 	NULL, /* stat */
210 	NULL  /* set_option */
211 };
212 
213  /* {{{ 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)214 static php_stream *php_glob_stream_opener(php_stream_wrapper *wrapper, const char *path, const char *mode,
215 		int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
216 {
217 	glob_s_t *pglob;
218 	int ret, i;
219 	const char *tmp, *pos;
220 
221 	if (!strncmp(path, "glob://", sizeof("glob://")-1)) {
222 		path += sizeof("glob://")-1;
223 		if (opened_path) {
224 			*opened_path = zend_string_init(path, strlen(path), 0);
225 		}
226 	}
227 
228 	pglob = ecalloc(1, sizeof(*pglob));
229 
230 	if (0 != (ret = glob(path, pglob->flags & GLOB_FLAGMASK, NULL, &pglob->glob))) {
231 #ifdef GLOB_NOMATCH
232 		if (GLOB_NOMATCH != ret)
233 #endif
234 		{
235 			efree(pglob);
236 			return NULL;
237 		}
238 	}
239 
240 	/* if open_basedir in use, check and filter restricted paths */
241 	if ((options & STREAM_DISABLE_OPEN_BASEDIR) == 0) {
242 		pglob->open_basedir_used = true;
243 		for (i = 0; i < pglob->glob.gl_pathc; i++) {
244 			if (!php_check_open_basedir_ex(pglob->glob.gl_pathv[i], 0)) {
245 				if (!pglob->open_basedir_indexmap) {
246 					pglob->open_basedir_indexmap = (size_t *) safe_emalloc(
247 							pglob->glob.gl_pathc, sizeof(size_t), 0);
248 				}
249 				pglob->open_basedir_indexmap[pglob->open_basedir_indexmap_size++] = i;
250 			}
251 		}
252 	}
253 
254 	pos = path;
255 	if ((tmp = strrchr(pos, '/')) != NULL) {
256 		pos = tmp+1;
257 	}
258 #ifdef PHP_WIN32
259 	if ((tmp = strrchr(pos, '\\')) != NULL) {
260 		pos = tmp+1;
261 	}
262 #endif
263 
264 	pglob->pattern_len = strlen(pos);
265 	pglob->pattern = estrndup(pos, pglob->pattern_len);
266 
267 	pglob->flags |= GLOB_APPEND;
268 
269 	if (pglob->glob.gl_pathc) {
270 		php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[0], 1, &tmp);
271 	} else {
272 		php_glob_stream_path_split(pglob, path, 1, &tmp);
273 	}
274 
275 	return php_stream_alloc(&php_glob_stream_ops, pglob, 0, mode);
276 }
277 /* }}} */
278 
279 static const php_stream_wrapper_ops  php_glob_stream_wrapper_ops = {
280 	NULL,
281 	NULL,
282 	NULL,
283 	NULL,
284 	php_glob_stream_opener,
285 	"glob",
286 	NULL,
287 	NULL,
288 	NULL,
289 	NULL,
290 	NULL
291 };
292 
293 const php_stream_wrapper  php_glob_stream_wrapper = {
294 	&php_glob_stream_wrapper_ops,
295 	NULL,
296 	0
297 };
298 #endif /* HAVE_GLOB */
299