xref: /PHP-7.2/Zend/zend_virtual_cwd.c (revision 81f52158)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Andi Gutmans <andi@zend.com>                                |
16    |          Sascha Schumann <sascha@schumann.cx>                        |
17    |          Pierre Joye <pierre@php.net>                                |
18    +----------------------------------------------------------------------+
19 */
20 
21 /* $Id$ */
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <limits.h>
28 #include <errno.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <time.h>
32 
33 #include "zend.h"
34 #include "zend_virtual_cwd.h"
35 
36 #ifdef ZEND_WIN32
37 #include <io.h>
38 #include "tsrm_win32.h"
39 # ifndef IO_REPARSE_TAG_SYMLINK
40 #  define IO_REPARSE_TAG_SYMLINK 0xA000000C
41 # endif
42 
43 # ifndef IO_REPARSE_TAG_DEDUP
44 #  define IO_REPARSE_TAG_DEDUP   0x80000013
45 # endif
46 
47 # ifndef IO_REPARSE_TAG_CLOUD
48 #  define IO_REPARSE_TAG_CLOUD    (0x9000001AL)
49 # endif
50 /* IO_REPARSE_TAG_CLOUD_1 through IO_REPARSE_TAG_CLOUD_F have values of 0x9000101AL
51    to 0x9000F01AL, they can be checked against the mask. */
52 #ifndef IO_REPARSE_TAG_CLOUD_MASK
53 #define IO_REPARSE_TAG_CLOUD_MASK (0x0000F000L)
54 #endif
55 
56 #ifndef IO_REPARSE_TAG_ONEDRIVE
57 #define IO_REPARSE_TAG_ONEDRIVE   (0x80000021L)
58 #endif
59 
60 # ifndef VOLUME_NAME_NT
61 #  define VOLUME_NAME_NT 0x2
62 # endif
63 
64 # ifndef VOLUME_NAME_DOS
65 #  define VOLUME_NAME_DOS 0x0
66 # endif
67 #endif
68 
69 #ifndef HAVE_REALPATH
70 #define realpath(x,y) strcpy(y,x)
71 #endif
72 
73 #define VIRTUAL_CWD_DEBUG 0
74 
75 #include "TSRM.h"
76 
77 /* Only need mutex for popen() in Windows because it doesn't chdir() on UNIX */
78 #if defined(ZEND_WIN32) && defined(ZTS)
79 MUTEX_T cwd_mutex;
80 #endif
81 
82 #ifdef ZTS
83 ts_rsrc_id cwd_globals_id;
84 #else
85 virtual_cwd_globals cwd_globals;
86 #endif
87 
88 cwd_state main_cwd_state; /* True global */
89 
90 #ifndef ZEND_WIN32
91 #include <unistd.h>
92 #else
93 #include <direct.h>
94 #include "zend_globals.h"
95 #include "zend_globals_macros.h"
96 #endif
97 
98 #define CWD_STATE_COPY(d, s)				\
99 	(d)->cwd_length = (s)->cwd_length;		\
100 	(d)->cwd = (char *) emalloc((s)->cwd_length+1);	\
101 	memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1);
102 
103 #define CWD_STATE_FREE(s)			\
104 	efree((s)->cwd); \
105 	(s)->cwd_length = 0;
106 
107 #ifdef ZEND_WIN32
108 # define CWD_STATE_FREE_ERR(state) do { \
109 		DWORD last_error = GetLastError(); \
110 		CWD_STATE_FREE(state); \
111 		SetLastError(last_error); \
112 	} while (0)
113 #else
114 # define CWD_STATE_FREE_ERR(state) CWD_STATE_FREE(state)
115 #endif
116 
117 #ifdef ZEND_WIN32
118 
119 #ifdef CTL_CODE
120 #undef CTL_CODE
121 #endif
122 #define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
123 #define FILE_DEVICE_FILE_SYSTEM 0x00000009
124 #define METHOD_BUFFERED		0
125 #define FILE_ANY_ACCESS 	0
126 #define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
127 #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )
128 
129 typedef struct {
130 	unsigned long  ReparseTag;
131 	unsigned short ReparseDataLength;
132 	unsigned short Reserved;
133 	union {
134 		struct {
135 			unsigned short SubstituteNameOffset;
136 			unsigned short SubstituteNameLength;
137 			unsigned short PrintNameOffset;
138 			unsigned short PrintNameLength;
139 			unsigned long  Flags;
140 			wchar_t        ReparseTarget[1];
141 		} SymbolicLinkReparseBuffer;
142 		struct {
143 			unsigned short SubstituteNameOffset;
144 			unsigned short SubstituteNameLength;
145 			unsigned short PrintNameOffset;
146 			unsigned short PrintNameLength;
147 			wchar_t        ReparseTarget[1];
148 		} MountPointReparseBuffer;
149 		struct {
150 			unsigned char  ReparseTarget[1];
151 		} GenericReparseBuffer;
152 	};
153 } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
154 
155 #define SECS_BETWEEN_EPOCHS (__int64)11644473600
156 #define SECS_TO_100NS (__int64)10000000
FileTimeToUnixTime(const FILETIME * FileTime)157 static inline time_t FileTimeToUnixTime(const FILETIME *FileTime)
158 {
159 	__int64 UnixTime;
160 	long *nsec = NULL;
161 	SYSTEMTIME SystemTime;
162 	FileTimeToSystemTime(FileTime, &SystemTime);
163 
164 	UnixTime = ((__int64)FileTime->dwHighDateTime << 32) +
165 	FileTime->dwLowDateTime;
166 
167 	UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);
168 
169 	if (nsec) {
170 		*nsec = (UnixTime % SECS_TO_100NS) * (__int64)100;
171 	}
172 
173 	UnixTime /= SECS_TO_100NS; /* now convert to seconds */
174 
175 	if ((time_t)UnixTime != UnixTime) {
176 		UnixTime = 0;
177 	}
178 	return (time_t)UnixTime;
179 }
180 
php_sys_readlink(const char * link,char * target,size_t target_len)181 CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */
182 	HANDLE hFile;
183 	wchar_t *linkw = php_win32_ioutil_any_to_w(link), targetw[MAXPATHLEN];
184 	size_t ret_len, targetw_len, offset = 0;
185 	char *ret;
186 
187 	if (!linkw) {
188 		return -1;
189 	}
190 
191 	if (!target_len) {
192 		free(linkw);
193 		return -1;
194 	}
195 
196 	hFile = CreateFileW(linkw,            // file to open
197 				 GENERIC_READ,          // open for reading
198 				 FILE_SHARE_READ,       // share for reading
199 				 NULL,                  // default security
200 				 OPEN_EXISTING,         // existing file only
201 				 FILE_FLAG_BACKUP_SEMANTICS, // normal file
202 				 NULL);                 // no attr. template
203 	if( hFile == INVALID_HANDLE_VALUE) {
204 		free(linkw);
205 		return -1;
206 	}
207 
208 	/* Despite MSDN has documented it won't to, the length returned by
209 		GetFinalPathNameByHandleA includes the length of the
210 		null terminator. This behavior is at least reproducible
211 		with VS2012 and earlier, and seems not to be fixed till
212 		now. Thus, correcting target_len so it's suddenly don't
213 		overflown. */
214 	targetw_len = GetFinalPathNameByHandleW(hFile, targetw, MAXPATHLEN, VOLUME_NAME_DOS);
215 	if(targetw_len >= target_len || targetw_len >= MAXPATHLEN || targetw_len == 0) {
216 		free(linkw);
217 		CloseHandle(hFile);
218 		return -1;
219 	}
220 
221 	if(targetw_len > 4) {
222 		/* Skip first 4 characters if they are "\\?\" */
223 		if(targetw[0] == L'\\' && targetw[1] == L'\\' && targetw[2] == L'?' && targetw[3] ==  L'\\') {
224 			offset = 4;
225 
226 			/* \\?\UNC\ */
227 			if (targetw_len > 7 && targetw[4] == L'U' && targetw[5] == L'N' && targetw[6] == L'C') {
228 				offset += 2;
229 				targetw[offset] = L'\\';
230 			}
231 		}
232 	}
233 
234 	ret = php_win32_ioutil_conv_w_to_any(targetw + offset, targetw_len - offset, &ret_len);
235 	if (!ret || ret_len >= MAXPATHLEN) {
236 		CloseHandle(hFile);
237 		free(linkw);
238 		free(ret);
239 		return -1;
240 	}
241 	memcpy(target, ret, ret_len + 1);
242 
243 	free(ret);
244 	CloseHandle(hFile);
245 	free(linkw);
246 
247 	return ret_len;
248 }
249 /* }}} */
250 
php_sys_stat_ex(const char * path,zend_stat_t * buf,int lstat)251 CWD_API int php_sys_stat_ex(const char *path, zend_stat_t *buf, int lstat) /* {{{ */
252 {
253 	WIN32_FILE_ATTRIBUTE_DATA data;
254 	LARGE_INTEGER t;
255 	size_t pathw_len = 0;
256 	ALLOCA_FLAG(use_heap_large)
257 	wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, PHP_WIN32_CP_IGNORE_LEN, &pathw_len);
258 
259 	if (!pathw) {
260 		return -1;
261 	}
262 
263 	if (!GetFileAttributesExW(pathw, GetFileExInfoStandard, &data)) {
264 		int ret;
265 #if ZEND_ENABLE_ZVAL_LONG64
266 		ret = _wstat64(pathw, buf);
267 #else
268 		ret = _wstat(pathw, (struct _stat32 *)buf);
269 #endif
270 		free(pathw);
271 
272 		return ret;
273 	}
274 
275 	if (pathw_len >= 1 && pathw[1] == L':') {
276 		if (pathw[0] >= L'A' && pathw[0] <= L'Z') {
277 			buf->st_dev = buf->st_rdev = pathw[0] - L'A';
278 		} else {
279 			buf->st_dev = buf->st_rdev = pathw[0] - L'a';
280 		}
281 	} else if (PHP_WIN32_IOUTIL_IS_UNC(pathw, pathw_len)) {
282 		buf->st_dev = buf->st_rdev = 0;
283 	} else {
284 		wchar_t cur_path[MAXPATHLEN+1];
285 
286 		if (NULL != _wgetcwd(cur_path, sizeof(cur_path)/sizeof(wchar_t))) {
287 			if (cur_path[1] == L':') {
288 				if (pathw[0] >= L'A' && pathw[0] <= L'Z') {
289 					buf->st_dev = buf->st_rdev = pathw[0] - L'A';
290 				} else {
291 					buf->st_dev = buf->st_rdev = pathw[0] - L'a';
292 				}
293 			} else {
294 				buf->st_dev = buf->st_rdev = -1;
295 			}
296 		} else {
297 			buf->st_dev = buf->st_rdev = -1;
298 		}
299 	}
300 
301 	buf->st_uid = buf->st_gid = buf->st_ino = 0;
302 
303 	if (lstat && data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
304 		/* File is a reparse point. Get the target */
305 		HANDLE hLink = NULL;
306 		REPARSE_DATA_BUFFER * pbuffer;
307 		DWORD retlength = 0;
308 
309 		hLink = CreateFileW(pathw, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
310 		if(hLink == INVALID_HANDLE_VALUE) {
311 			free(pathw);
312 			return -1;
313 		}
314 
315 		pbuffer = (REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
316 		if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
317 			free_alloca(pbuffer, use_heap_large);
318 			CloseHandle(hLink);
319 			free(pathw);
320 			return -1;
321 		}
322 
323 		CloseHandle(hLink);
324 
325 		if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
326 			buf->st_mode = S_IFLNK;
327 			buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
328 		}
329 
330 #if 0 /* Not used yet */
331 		else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
332 			buf->st_mode |=;
333 		}
334 #endif
335 		free_alloca(pbuffer, use_heap_large);
336 	} else {
337 		buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
338 		buf->st_mode |= (data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) ? (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)) : (S_IREAD|(S_IREAD>>3)|(S_IREAD>>6)|S_IWRITE|(S_IWRITE>>3)|(S_IWRITE>>6));
339 	}
340 
341 	if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
342 		if (pathw_len >= 4 && pathw[pathw_len-4] == L'.') {
343 			if (_wcsnicmp(pathw+pathw_len-3, L"exe", 3) == 0 ||
344 				_wcsnicmp(pathw+pathw_len-3, L"com", 3) == 0 ||
345 				_wcsnicmp(pathw+pathw_len-3, L"bat", 3) == 0 ||
346 				_wcsnicmp(pathw+pathw_len-3, L"cmd", 3) == 0) {
347 				buf->st_mode  |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
348 			}
349 		}
350 	}
351 
352 	buf->st_nlink = 1;
353 	t.HighPart = data.nFileSizeHigh;
354 	t.LowPart = data.nFileSizeLow;
355 	/* It's an overflow on 32 bit, however it won't fix as long
356 	as zend_long is 32 bit. */
357 	buf->st_size = (zend_long)t.QuadPart;
358 	buf->st_atime = FileTimeToUnixTime(&data.ftLastAccessTime);
359 	buf->st_ctime = FileTimeToUnixTime(&data.ftCreationTime);
360 	buf->st_mtime = FileTimeToUnixTime(&data.ftLastWriteTime);
361 
362 	free(pathw);
363 
364 	return 0;
365 }
366 /* }}} */
367 #endif
368 
php_is_dir_ok(const cwd_state * state)369 static int php_is_dir_ok(const cwd_state *state)  /* {{{ */
370 {
371 	zend_stat_t buf;
372 
373 	if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode))
374 		return (0);
375 
376 	return (1);
377 }
378 /* }}} */
379 
php_is_file_ok(const cwd_state * state)380 static int php_is_file_ok(const cwd_state *state)  /* {{{ */
381 {
382 	zend_stat_t buf;
383 
384 	if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode))
385 		return (0);
386 
387 	return (1);
388 }
389 /* }}} */
390 
cwd_globals_ctor(virtual_cwd_globals * cwd_g)391 static void cwd_globals_ctor(virtual_cwd_globals *cwd_g) /* {{{ */
392 {
393 	CWD_STATE_COPY(&cwd_g->cwd, &main_cwd_state);
394 	cwd_g->realpath_cache_size = 0;
395 	cwd_g->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
396 	cwd_g->realpath_cache_ttl = REALPATH_CACHE_TTL;
397 	memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache));
398 }
399 /* }}} */
400 
cwd_globals_dtor(virtual_cwd_globals * cwd_g)401 static void cwd_globals_dtor(virtual_cwd_globals *cwd_g) /* {{{ */
402 {
403 	realpath_cache_clean();
404 }
405 /* }}} */
406 
virtual_cwd_main_cwd_init(uint8_t reinit)407 void virtual_cwd_main_cwd_init(uint8_t reinit) /* {{{ */
408 {
409 	char cwd[MAXPATHLEN];
410 	char *result;
411 
412 	if (reinit) {
413 		free(main_cwd_state.cwd);
414 	}
415 
416 #ifdef ZEND_WIN32
417 	ZeroMemory(&cwd, sizeof(cwd));
418 	result = php_win32_ioutil_getcwd(cwd, sizeof(cwd));
419 #else
420 	result = getcwd(cwd, sizeof(cwd));
421 #endif
422 
423 	if (!result) {
424 		cwd[0] = '\0';
425 	}
426 
427 	main_cwd_state.cwd_length = (int)strlen(cwd);
428 #ifdef ZEND_WIN32
429 	if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') {
430 		cwd[0] = toupper(cwd[0]);
431 	}
432 #endif
433 	main_cwd_state.cwd = strdup(cwd);
434 }
435 /* }}} */
436 
virtual_cwd_startup(void)437 CWD_API void virtual_cwd_startup(void) /* {{{ */
438 {
439 	virtual_cwd_main_cwd_init(0);
440 #ifdef ZTS
441 	ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor);
442 #else
443 	cwd_globals_ctor(&cwd_globals);
444 #endif
445 
446 #if (defined(ZEND_WIN32)) && defined(ZTS)
447 	cwd_mutex = tsrm_mutex_alloc();
448 #endif
449 }
450 /* }}} */
451 
virtual_cwd_shutdown(void)452 CWD_API void virtual_cwd_shutdown(void) /* {{{ */
453 {
454 #ifndef ZTS
455 	cwd_globals_dtor(&cwd_globals);
456 #endif
457 #if (defined(ZEND_WIN32)) && defined(ZTS)
458 	tsrm_mutex_free(cwd_mutex);
459 #endif
460 
461 	free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */
462 }
463 /* }}} */
464 
virtual_cwd_activate(void)465 CWD_API int virtual_cwd_activate(void) /* {{{ */
466 {
467 	if (CWDG(cwd).cwd == NULL) {
468 		CWD_STATE_COPY(&CWDG(cwd), &main_cwd_state);
469 	}
470 	return 0;
471 }
472 /* }}} */
473 
virtual_cwd_deactivate(void)474 CWD_API int virtual_cwd_deactivate(void) /* {{{ */
475 {
476 	if (CWDG(cwd).cwd != NULL) {
477 		CWD_STATE_FREE(&CWDG(cwd));
478 		CWDG(cwd).cwd = NULL;
479 	}
480 	return 0;
481 }
482 /* }}} */
483 
virtual_getcwd_ex(size_t * length)484 CWD_API char *virtual_getcwd_ex(size_t *length) /* {{{ */
485 {
486 	cwd_state *state;
487 
488 	state = &CWDG(cwd);
489 
490 	if (state->cwd_length == 0) {
491 		char *retval;
492 
493 		*length = 1;
494 		retval = (char *) emalloc(2);
495 		retval[0] = DEFAULT_SLASH;
496 		retval[1] = '\0';
497 		return retval;
498 	}
499 
500 #ifdef ZEND_WIN32
501 	/* If we have something like C: */
502 	if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') {
503 		char *retval;
504 
505 		*length = state->cwd_length+1;
506 		retval = (char *) emalloc(*length+1);
507 		memcpy(retval, state->cwd, *length);
508 		retval[0] = toupper(retval[0]);
509 		retval[*length-1] = DEFAULT_SLASH;
510 		retval[*length] = '\0';
511 		return retval;
512 	}
513 #endif
514 	if (!state->cwd) {
515 		*length = 0;
516 		return NULL;
517 	}
518 
519 	*length = state->cwd_length;
520 	return estrdup(state->cwd);
521 }
522 /* }}} */
523 
524 /* Same semantics as UNIX getcwd() */
virtual_getcwd(char * buf,size_t size)525 CWD_API char *virtual_getcwd(char *buf, size_t size) /* {{{ */
526 {
527 	size_t length;
528 	char *cwd;
529 
530 	cwd = virtual_getcwd_ex(&length);
531 
532 	if (buf == NULL) {
533 		return cwd;
534 	}
535 	if (length > size-1) {
536 		efree(cwd);
537 		errno = ERANGE; /* Is this OK? */
538 		return NULL;
539 	}
540 	if (!cwd) {
541 		return NULL;
542 	}
543 	memcpy(buf, cwd, length+1);
544 	efree(cwd);
545 	return buf;
546 }
547 /* }}} */
548 
549 #ifdef ZEND_WIN32
realpath_cache_key(const char * path,size_t path_len)550 static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /* {{{ */
551 {
552 	register zend_ulong h;
553 	size_t bucket_key_len;
554 	char *bucket_key_start = tsrm_win32_get_path_sid_key(path, path_len, &bucket_key_len);
555 	char *bucket_key = (char *)bucket_key_start;
556 	const char *e;
557 
558 	if (!bucket_key) {
559 		return 0;
560 	}
561 
562 	e = bucket_key + bucket_key_len;
563 	for (h = Z_UL(2166136261); bucket_key < e;) {
564 		h *= Z_UL(16777619);
565 		h ^= *bucket_key++;
566 	}
567 	if (bucket_key_start != path) {
568 		HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start);
569 	}
570 	return h;
571 }
572 /* }}} */
573 #else
realpath_cache_key(const char * path,size_t path_len)574 static inline zend_ulong realpath_cache_key(const char *path, size_t path_len) /* {{{ */
575 {
576 	register zend_ulong h;
577 	const char *e = path + path_len;
578 
579 	for (h = Z_UL(2166136261); path < e;) {
580 		h *= Z_UL(16777619);
581 		h ^= *path++;
582 	}
583 
584 	return h;
585 }
586 /* }}} */
587 #endif /* defined(ZEND_WIN32) */
588 
realpath_cache_clean(void)589 CWD_API void realpath_cache_clean(void) /* {{{ */
590 {
591 	uint32_t i;
592 
593 	for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) {
594 		realpath_cache_bucket *p = CWDG(realpath_cache)[i];
595 		while (p != NULL) {
596 			realpath_cache_bucket *r = p;
597 			p = p->next;
598 			free(r);
599 		}
600 		CWDG(realpath_cache)[i] = NULL;
601 	}
602 	CWDG(realpath_cache_size) = 0;
603 }
604 /* }}} */
605 
realpath_cache_del(const char * path,size_t path_len)606 CWD_API void realpath_cache_del(const char *path, size_t path_len) /* {{{ */
607 {
608 	zend_ulong key = realpath_cache_key(path, path_len);
609 	zend_ulong n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
610 	realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
611 
612 	while (*bucket != NULL) {
613 		if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
614 					memcmp(path, (*bucket)->path, path_len) == 0) {
615 			realpath_cache_bucket *r = *bucket;
616 			*bucket = (*bucket)->next;
617 
618 			/* if the pointers match then only subtract the length of the path */
619 		   	if(r->path == r->realpath) {
620 				CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1;
621 			} else {
622 				CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
623 			}
624 
625 			free(r);
626 			return;
627 		} else {
628 			bucket = &(*bucket)->next;
629 		}
630 	}
631 }
632 /* }}} */
633 
realpath_cache_add(const char * path,size_t path_len,const char * realpath,size_t realpath_len,int is_dir,time_t t)634 static inline void realpath_cache_add(const char *path, size_t path_len, const char *realpath, size_t realpath_len, int is_dir, time_t t) /* {{{ */
635 {
636 	zend_long size = sizeof(realpath_cache_bucket) + path_len + 1;
637 	int same = 1;
638 
639 	if (realpath_len != path_len ||
640 		memcmp(path, realpath, path_len) != 0) {
641 		size += realpath_len + 1;
642 		same = 0;
643 	}
644 
645 	if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
646 		realpath_cache_bucket *bucket = malloc(size);
647 		zend_ulong n;
648 
649 		if (bucket == NULL) {
650 			return;
651 		}
652 
653 		bucket->key = realpath_cache_key(path, path_len);
654 		bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
655 		memcpy(bucket->path, path, path_len+1);
656 		bucket->path_len = path_len;
657 		if (same) {
658 			bucket->realpath = bucket->path;
659 		} else {
660 			bucket->realpath = bucket->path + (path_len + 1);
661 			memcpy(bucket->realpath, realpath, realpath_len+1);
662 		}
663 		bucket->realpath_len = realpath_len;
664 		bucket->is_dir = is_dir > 0;
665 #ifdef ZEND_WIN32
666 		bucket->is_rvalid   = 0;
667 		bucket->is_readable = 0;
668 		bucket->is_wvalid   = 0;
669 		bucket->is_writable = 0;
670 #endif
671 		bucket->expires = t + CWDG(realpath_cache_ttl);
672 		n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
673 		bucket->next = CWDG(realpath_cache)[n];
674 		CWDG(realpath_cache)[n] = bucket;
675 		CWDG(realpath_cache_size) += size;
676 	}
677 }
678 /* }}} */
679 
realpath_cache_find(const char * path,size_t path_len,time_t t)680 static inline realpath_cache_bucket* realpath_cache_find(const char *path, size_t path_len, time_t t) /* {{{ */
681 {
682 	zend_ulong key = realpath_cache_key(path, path_len);
683 	zend_ulong n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
684 	realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];
685 
686 	while (*bucket != NULL) {
687 		if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
688 			realpath_cache_bucket *r = *bucket;
689 			*bucket = (*bucket)->next;
690 
691 			/* if the pointers match then only subtract the length of the path */
692 		   	if(r->path == r->realpath) {
693 				CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1;
694 			} else {
695 				CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
696 			}
697 			free(r);
698 		} else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
699 					memcmp(path, (*bucket)->path, path_len) == 0) {
700 			return *bucket;
701 		} else {
702 			bucket = &(*bucket)->next;
703 		}
704 	}
705 	return NULL;
706 }
707 /* }}} */
708 
realpath_cache_lookup(const char * path,size_t path_len,time_t t)709 CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, size_t path_len, time_t t) /* {{{ */
710 {
711 	return realpath_cache_find(path, path_len, t);
712 }
713 /* }}} */
714 
realpath_cache_size(void)715 CWD_API zend_long realpath_cache_size(void)
716 {
717 	return CWDG(realpath_cache_size);
718 }
719 
realpath_cache_max_buckets(void)720 CWD_API zend_long realpath_cache_max_buckets(void)
721 {
722 	return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
723 }
724 
realpath_cache_get_buckets(void)725 CWD_API realpath_cache_bucket** realpath_cache_get_buckets(void)
726 {
727 	return CWDG(realpath_cache);
728 }
729 
730 
731 #undef LINK_MAX
732 #define LINK_MAX 32
733 
tsrm_realpath_r(char * path,int start,int len,int * ll,time_t * t,int use_realpath,int is_dir,int * link_is_dir)734 static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, int use_realpath, int is_dir, int *link_is_dir) /* {{{ */
735 {
736 	int i, j, save;
737 	int directory = 0;
738 #ifdef ZEND_WIN32
739 	WIN32_FIND_DATAW dataw;
740 	HANDLE hFind = INVALID_HANDLE_VALUE;
741 	ALLOCA_FLAG(use_heap_large)
742 	wchar_t *pathw = NULL;
743 #define FREE_PATHW() \
744 	do { free(pathw); } while(0);
745 
746 #else
747 	zend_stat_t st;
748 #endif
749 	realpath_cache_bucket *bucket;
750 	char *tmp;
751 	ALLOCA_FLAG(use_heap)
752 
753 	while (1) {
754 		if (len <= start) {
755 			if (link_is_dir) {
756 				*link_is_dir = 1;
757 			}
758 			return start;
759 		}
760 
761 		i = len;
762 		while (i > start && !IS_SLASH(path[i-1])) {
763 			i--;
764 		}
765 
766 		if (i == len ||
767 			(i == len - 1 && path[i] == '.')) {
768 			/* remove double slashes and '.' */
769 			len = i - 1;
770 			is_dir = 1;
771 			continue;
772 		} else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') {
773 			/* remove '..' and previous directory */
774 			is_dir = 1;
775 			if (link_is_dir) {
776 				*link_is_dir = 1;
777 			}
778 			if (i - 1 <= start) {
779 				return start ? start : len;
780 			}
781 			j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL);
782 			if (j > start) {
783 				j--;
784 				while (j > start && !IS_SLASH(path[j])) {
785 					j--;
786 				}
787 				if (!start) {
788 					/* leading '..' must not be removed in case of relative path */
789 					if (j == 0 && path[0] == '.' && path[1] == '.' &&
790 							IS_SLASH(path[2])) {
791 						path[3] = '.';
792 						path[4] = '.';
793 						path[5] = DEFAULT_SLASH;
794 						j = 5;
795 					} else if (j > 0 &&
796 							path[j+1] == '.' && path[j+2] == '.' &&
797 							IS_SLASH(path[j+3])) {
798 						j += 4;
799 						path[j++] = '.';
800 						path[j++] = '.';
801 						path[j] = DEFAULT_SLASH;
802 					}
803 				}
804 			} else if (!start && !j) {
805 				/* leading '..' must not be removed in case of relative path */
806 				path[0] = '.';
807 				path[1] = '.';
808 				path[2] = DEFAULT_SLASH;
809 				j = 2;
810 			}
811 			return j;
812 		}
813 
814 		path[len] = 0;
815 
816 		save = (use_realpath != CWD_EXPAND);
817 
818 		if (start && save && CWDG(realpath_cache_size_limit)) {
819 			/* cache lookup for absolute path */
820 			if (!*t) {
821 				*t = time(0);
822 			}
823 			if ((bucket = realpath_cache_find(path, len, *t)) != NULL) {
824 				if (is_dir && !bucket->is_dir) {
825 					/* not a directory */
826 					return -1;
827 				} else {
828 					if (link_is_dir) {
829 						*link_is_dir = bucket->is_dir;
830 					}
831 					memcpy(path, bucket->realpath, bucket->realpath_len + 1);
832 					return bucket->realpath_len;
833 				}
834 			}
835 		}
836 
837 #ifdef ZEND_WIN32
838 		if (save) {
839 			pathw = php_win32_ioutil_any_to_w(path);
840 			if (!pathw) {
841 				return -1;
842 			}
843 			hFind = FindFirstFileExW(pathw, FindExInfoBasic, &dataw, FindExSearchNameMatch, NULL, 0);
844 			if (INVALID_HANDLE_VALUE == hFind) {
845 				if (use_realpath == CWD_REALPATH) {
846 					/* file not found */
847 					FREE_PATHW()
848 					return -1;
849 				}
850 				/* continue resolution anyway but don't save result in the cache */
851 				save = 0;
852 			} else {
853 				FindClose(hFind);
854 			}
855 		}
856 
857 		tmp = do_alloca(len+1, use_heap);
858 		memcpy(tmp, path, len+1);
859 
860 retry:
861 		if(save &&
862 				!(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') &&
863                                (dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
864 				) {
865 			/* File is a reparse point. Get the target */
866 			HANDLE hLink = NULL;
867 			REPARSE_DATA_BUFFER * pbuffer;
868 			DWORD retlength = 0;
869 			int bufindex = 0, isabsolute = 0;
870 			wchar_t * reparsetarget;
871 			BOOL isVolume = FALSE;
872 #if VIRTUAL_CWD_DEBUG
873 			char *printname = NULL;
874 #endif
875 			char *substitutename = NULL;
876 			size_t substitutename_len;
877 			int substitutename_off = 0;
878 			wchar_t tmpsubstname[MAXPATHLEN];
879 
880 			if(++(*ll) > LINK_MAX) {
881 				free_alloca(tmp, use_heap);
882 				FREE_PATHW()
883 				return -1;
884 			}
885 
886 			hLink = CreateFileW(pathw, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
887 			if(hLink == INVALID_HANDLE_VALUE) {
888 				free_alloca(tmp, use_heap);
889 				FREE_PATHW()
890 				return -1;
891 			}
892 
893 			pbuffer = (REPARSE_DATA_BUFFER *)do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
894 			if (pbuffer == NULL) {
895 				CloseHandle(hLink);
896 				free_alloca(tmp, use_heap);
897 				FREE_PATHW()
898 				return -1;
899 			}
900 			if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
901 				BY_HANDLE_FILE_INFORMATION fileInformation;
902 
903 				free_alloca(pbuffer, use_heap_large);
904 				if ((dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
905 						(dataw.dwReserved0 & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD &&
906 						EG(windows_version_info).dwMajorVersion >= 10 &&
907 						EG(windows_version_info).dwMinorVersion == 0 &&
908 						EG(windows_version_info).dwBuildNumber >= 18362 &&
909 						GetFileInformationByHandle(hLink, &fileInformation) &&
910 						!(fileInformation.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
911 					dataw.dwFileAttributes = fileInformation.dwFileAttributes;
912 					CloseHandle(hLink);
913 					(*ll)--;
914 					goto retry;
915 				}
916 				free_alloca(tmp, use_heap);
917 				CloseHandle(hLink);
918 				FREE_PATHW()
919 				return -1;
920 			}
921 
922 			CloseHandle(hLink);
923 
924 			if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
925 				reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
926 				isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0;
927 #if VIRTUAL_CWD_DEBUG
928 				printname = php_win32_ioutil_w_to_any(reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset  / sizeof(WCHAR));
929 				if (!printname) {
930 					free_alloca(pbuffer, use_heap_large);
931 					free_alloca(tmp, use_heap);
932 					FREE_PATHW()
933 					return -1;
934 				}
935 #endif
936 
937 				substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
938 				if (substitutename_len >= MAXPATHLEN) {
939 					free_alloca(pbuffer, use_heap_large);
940 					free_alloca(tmp, use_heap);
941 					FREE_PATHW()
942 					return -1;
943 				}
944 				memmove(tmpsubstname, reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), pbuffer->MountPointReparseBuffer.SubstituteNameLength);
945 				tmpsubstname[substitutename_len] = L'\0';
946 				substitutename = php_win32_cp_conv_w_to_any(tmpsubstname, substitutename_len, &substitutename_len);
947 				if (!substitutename || substitutename_len >= MAXPATHLEN) {
948 					free_alloca(pbuffer, use_heap_large);
949 					free_alloca(tmp, use_heap);
950 					free(substitutename);
951 #if VIRTUAL_CWD_DEBUG
952 					free(printname);
953 #endif
954 					FREE_PATHW()
955 					return -1;
956 				}
957 			}
958 			else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
959 				isabsolute = 1;
960 				reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
961 #if VIRTUAL_CWD_DEBUG
962 				printname = php_win32_ioutil_w_to_any(reparsetarget + pbuffer->MountPointReparseBuffer.PrintNameOffset  / sizeof(WCHAR));
963 				if (!printname) {
964 					free_alloca(pbuffer, use_heap_large);
965 					free_alloca(tmp, use_heap);
966 					FREE_PATHW()
967 					return -1;
968 				}
969 #endif
970 
971 
972 				substitutename_len = pbuffer->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR);
973 				if (substitutename_len >= MAXPATHLEN) {
974 					free_alloca(pbuffer, use_heap_large);
975 					free_alloca(tmp, use_heap);
976 					FREE_PATHW()
977 					return -1;
978 				}
979 				memmove(tmpsubstname, reparsetarget + pbuffer->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR), pbuffer->MountPointReparseBuffer.SubstituteNameLength);
980 				tmpsubstname[substitutename_len] = L'\0';
981 				substitutename = php_win32_cp_conv_w_to_any(tmpsubstname, substitutename_len, &substitutename_len);
982 				if (!substitutename || substitutename_len >= MAXPATHLEN) {
983 					free_alloca(pbuffer, use_heap_large);
984 					free_alloca(tmp, use_heap);
985 					free(substitutename);
986 #if VIRTUAL_CWD_DEBUG
987 					free(printname);
988 #endif
989 					FREE_PATHW()
990 					return -1;
991 				}
992 			}
993 			else if (pbuffer->ReparseTag == IO_REPARSE_TAG_DEDUP ||
994 					/* Starting with 1709. */
995 					(pbuffer->ReparseTag & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD ||
996 					IO_REPARSE_TAG_ONEDRIVE == pbuffer->ReparseTag) {
997 				isabsolute = 1;
998 				substitutename = malloc((len + 1) * sizeof(char));
999 				if (!substitutename) {
1000 					free_alloca(pbuffer, use_heap_large);
1001 					free_alloca(tmp, use_heap);
1002 					FREE_PATHW()
1003 					return -1;
1004 				}
1005 				memcpy(substitutename, path, len + 1);
1006 				substitutename_len = len;
1007 			} else {
1008 				/* XXX this might be not the end, restart handling with REPARSE_GUID_DATA_BUFFER should be implemented. */
1009 				free_alloca(pbuffer, use_heap_large);
1010 				free_alloca(tmp, use_heap);
1011 				FREE_PATHW()
1012 				return -1;
1013 			}
1014 
1015 			if(isabsolute && substitutename_len > 4) {
1016 				/* Do not resolve volumes (for now). A mounted point can
1017 				   target a volume without a drive, it is not certain that
1018 				   all IO functions we use in php and its deps support
1019 				   path with volume GUID instead of the DOS way, like:
1020 				   d:\test\mnt\foo
1021 				   \\?\Volume{62d1c3f8-83b9-11de-b108-806e6f6e6963}\foo
1022 				*/
1023 				if (strncmp(substitutename, "\\??\\Volume{",11) == 0
1024 					|| strncmp(substitutename, "\\\\?\\Volume{",11) == 0
1025 					|| strncmp(substitutename, "\\??\\UNC\\", 8) == 0
1026 					) {
1027 					isVolume = TRUE;
1028 					substitutename_off = 0;
1029 				} else
1030 					/* do not use the \??\ and \\?\ prefix*/
1031 					if (strncmp(substitutename, "\\??\\", 4) == 0
1032 						|| strncmp(substitutename, "\\\\?\\", 4) == 0) {
1033 					substitutename_off = 4;
1034 				}
1035 			}
1036 
1037 			if (!isVolume) {
1038 				char * tmp2 = substitutename + substitutename_off;
1039 				for(bufindex = 0; bufindex < (substitutename_len - substitutename_off); bufindex++) {
1040 					*(path + bufindex) = *(tmp2 + bufindex);
1041 				}
1042 
1043 				*(path + bufindex) = 0;
1044 				j = bufindex;
1045 			} else {
1046 				j = len;
1047 			}
1048 
1049 
1050 #if VIRTUAL_CWD_DEBUG
1051 			fprintf(stderr, "reparse: print: %s ", printname);
1052 			fprintf(stderr, "sub: %s ", substitutename);
1053 			fprintf(stderr, "resolved: %s ", path);
1054 			free(printname);
1055 #endif
1056 			free_alloca(pbuffer, use_heap_large);
1057 			free(substitutename);
1058 
1059 			if(isabsolute == 1) {
1060 				if (!((j == 3) && (path[1] == ':') && (path[2] == '\\'))) {
1061 					/* use_realpath is 0 in the call below coz path is absolute*/
1062 					j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory);
1063 					if(j < 0) {
1064 						free_alloca(tmp, use_heap);
1065 						FREE_PATHW()
1066 						return -1;
1067 					}
1068 				}
1069 			}
1070 			else {
1071 				if(i + j >= MAXPATHLEN - 1) {
1072 					free_alloca(tmp, use_heap);
1073 					FREE_PATHW()
1074 					return -1;
1075 				}
1076 
1077 				memmove(path+i, path, j+1);
1078 				memcpy(path, tmp, i-1);
1079 				path[i-1] = DEFAULT_SLASH;
1080 				j  = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory);
1081 				if(j < 0) {
1082 					free_alloca(tmp, use_heap);
1083 					FREE_PATHW()
1084 					return -1;
1085 				}
1086 			}
1087 			directory = (dataw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1088 
1089 			if(link_is_dir) {
1090 				*link_is_dir = directory;
1091 			}
1092 		}
1093 		else {
1094 			if (save) {
1095 				directory = (dataw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1096 				if (is_dir && !directory) {
1097 					/* not a directory */
1098 					free_alloca(tmp, use_heap);
1099 					FREE_PATHW()
1100 					return -1;
1101 				}
1102 			}
1103 #else
1104 		if (save && php_sys_lstat(path, &st) < 0) {
1105 			if (use_realpath == CWD_REALPATH) {
1106 				/* file not found */
1107 				return -1;
1108 			}
1109 			/* continue resolution anyway but don't save result in the cache */
1110 			save = 0;
1111 		}
1112 
1113 		tmp = do_alloca(len+1, use_heap);
1114 		memcpy(tmp, path, len+1);
1115 
1116 		if (save && S_ISLNK(st.st_mode)) {
1117 			if (++(*ll) > LINK_MAX || (j = php_sys_readlink(tmp, path, MAXPATHLEN)) < 0) {
1118 				/* too many links or broken symlinks */
1119 				free_alloca(tmp, use_heap);
1120 				return -1;
1121 			}
1122 			path[j] = 0;
1123 			if (IS_ABSOLUTE_PATH(path, j)) {
1124 				j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory);
1125 				if (j < 0) {
1126 					free_alloca(tmp, use_heap);
1127 					return -1;
1128 				}
1129 			} else {
1130 				if (i + j >= MAXPATHLEN-1) {
1131 					free_alloca(tmp, use_heap);
1132 					return -1; /* buffer overflow */
1133 				}
1134 				memmove(path+i, path, j+1);
1135 				memcpy(path, tmp, i-1);
1136 				path[i-1] = DEFAULT_SLASH;
1137 				j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory);
1138 				if (j < 0) {
1139 					free_alloca(tmp, use_heap);
1140 					return -1;
1141 				}
1142 			}
1143 			if (link_is_dir) {
1144 				*link_is_dir = directory;
1145 			}
1146 		} else {
1147 			if (save) {
1148 				directory = S_ISDIR(st.st_mode);
1149 				if (link_is_dir) {
1150 					*link_is_dir = directory;
1151 				}
1152 				if (is_dir && !directory) {
1153 					/* not a directory */
1154 					free_alloca(tmp, use_heap);
1155 					return -1;
1156 				}
1157 			}
1158 #endif
1159 			if (i - 1 <= start) {
1160 				j = start;
1161 			} else {
1162 				/* some leading directories may be unaccessable */
1163 				j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL);
1164 				if (j > start) {
1165 					path[j++] = DEFAULT_SLASH;
1166 				}
1167 			}
1168 #ifdef ZEND_WIN32
1169 			if (j < 0 || j + len - i >= MAXPATHLEN-1) {
1170 				free_alloca(tmp, use_heap);
1171 				FREE_PATHW()
1172 				return -1;
1173 			}
1174 			if (save) {
1175 				size_t sz;
1176 				char *tmp_path = php_win32_ioutil_conv_w_to_any(dataw.cFileName, PHP_WIN32_CP_IGNORE_LEN, &sz);
1177 				if (!tmp_path) {
1178 					free_alloca(tmp, use_heap);
1179 					FREE_PATHW()
1180 					return -1;
1181 				}
1182 				i = (int)sz;
1183 				memcpy(path+j, tmp_path, i+1);
1184 				free(tmp_path);
1185 				j += i;
1186 			} else {
1187 				/* use the original file or directory name as it wasn't found */
1188 				memcpy(path+j, tmp+i, len-i+1);
1189 				j += (len-i);
1190 			}
1191 		}
1192 #else
1193 			if (j < 0 || j + len - i >= MAXPATHLEN-1) {
1194 				free_alloca(tmp, use_heap);
1195 				return -1;
1196 			}
1197 			memcpy(path+j, tmp+i, len-i+1);
1198 			j += (len-i);
1199 		}
1200 #endif
1201 
1202 		if (save && start && CWDG(realpath_cache_size_limit)) {
1203 			/* save absolute path in the cache */
1204 			realpath_cache_add(tmp, len, path, j, directory, *t);
1205 		}
1206 
1207 		free_alloca(tmp, use_heap);
1208 #ifdef ZEND_WIN32
1209 		FREE_PATHW()
1210 #undef FREE_PATHW
1211 #endif
1212 		return j;
1213 	}
1214 }
1215 /* }}} */
1216 
1217 /* Resolve path relatively to state and put the real path into state */
1218 /* returns 0 for ok, 1 for error */
virtual_file_ex(cwd_state * state,const char * path,verify_path_func verify_path,int use_realpath)1219 CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
1220 {
1221 	int path_length = (int)strlen(path);
1222 	char resolved_path[MAXPATHLEN];
1223 	int start = 1;
1224 	int ll = 0;
1225 	time_t t;
1226 	int ret;
1227 	int add_slash;
1228 	void *tmp;
1229 
1230 	if (path_length <= 0 || path_length >= MAXPATHLEN-1) {
1231 #ifdef ZEND_WIN32
1232 		_set_errno(EINVAL);
1233 #else
1234 		errno = EINVAL;
1235 #endif
1236 		return 1;
1237 	}
1238 
1239 #if VIRTUAL_CWD_DEBUG
1240 	fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
1241 #endif
1242 
1243 	/* cwd_length can be 0 when getcwd() fails.
1244 	 * This can happen under solaris when a dir does not have read permissions
1245 	 * but *does* have execute permissions */
1246 	if (!IS_ABSOLUTE_PATH(path, path_length)) {
1247 		if (state->cwd_length == 0) {
1248 			/* resolve relative path */
1249 			start = 0;
1250 			memcpy(resolved_path , path, path_length + 1);
1251 		} else {
1252 			int state_cwd_length = state->cwd_length;
1253 
1254 #ifdef ZEND_WIN32
1255 			if (IS_SLASH(path[0])) {
1256 				if (state->cwd[1] == ':') {
1257 					/* Copy only the drive name */
1258 					state_cwd_length = 2;
1259 				} else if (IS_UNC_PATH(state->cwd, state->cwd_length)) {
1260 					/* Copy only the share name */
1261 					state_cwd_length = 2;
1262 					while (IS_SLASH(state->cwd[state_cwd_length])) {
1263 						state_cwd_length++;
1264 					}
1265 					while (state->cwd[state_cwd_length] &&
1266 							!IS_SLASH(state->cwd[state_cwd_length])) {
1267 						state_cwd_length++;
1268 					}
1269 					while (IS_SLASH(state->cwd[state_cwd_length])) {
1270 						state_cwd_length++;
1271 					}
1272 					while (state->cwd[state_cwd_length] &&
1273 							!IS_SLASH(state->cwd[state_cwd_length])) {
1274 						state_cwd_length++;
1275 					}
1276 				}
1277 			}
1278 #endif
1279 			if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) {
1280 				return 1;
1281 			}
1282 			memcpy(resolved_path, state->cwd, state_cwd_length);
1283 			if (resolved_path[state_cwd_length-1] == DEFAULT_SLASH) {
1284 				memcpy(resolved_path + state_cwd_length, path, path_length + 1);
1285 				path_length += state_cwd_length;
1286 			} else {
1287 				resolved_path[state_cwd_length] = DEFAULT_SLASH;
1288 				memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1);
1289 				path_length += state_cwd_length + 1;
1290 			}
1291 		}
1292 	} else {
1293 #ifdef ZEND_WIN32
1294 		if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) {
1295 			resolved_path[0] = path[0];
1296 			resolved_path[1] = ':';
1297 			resolved_path[2] = DEFAULT_SLASH;
1298 			memcpy(resolved_path + 3, path + 2, path_length - 1);
1299 			path_length++;
1300 		} else
1301 #endif
1302 		memcpy(resolved_path, path, path_length + 1);
1303 	}
1304 
1305 #ifdef ZEND_WIN32
1306 	if (memchr(resolved_path, '*', path_length) ||
1307 		memchr(resolved_path, '?', path_length)) {
1308 		return 1;
1309 	}
1310 #endif
1311 
1312 #ifdef ZEND_WIN32
1313 	if (IS_UNC_PATH(resolved_path, path_length)) {
1314 		/* skip UNC name */
1315 		resolved_path[0] = DEFAULT_SLASH;
1316 		resolved_path[1] = DEFAULT_SLASH;
1317 		start = 2;
1318 		while (!IS_SLASH(resolved_path[start])) {
1319 			if (resolved_path[start] == 0) {
1320 				goto verify;
1321 			}
1322 			resolved_path[start] = toupper(resolved_path[start]);
1323 			start++;
1324 		}
1325 		resolved_path[start++] = DEFAULT_SLASH;
1326 		while (!IS_SLASH(resolved_path[start])) {
1327 			if (resolved_path[start] == 0) {
1328 				goto verify;
1329 			}
1330 			resolved_path[start] = toupper(resolved_path[start]);
1331 			start++;
1332 		}
1333 		resolved_path[start++] = DEFAULT_SLASH;
1334 	} else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
1335 		/* skip DRIVE name */
1336 		resolved_path[0] = toupper(resolved_path[0]);
1337 		resolved_path[2] = DEFAULT_SLASH;
1338 		start = 3;
1339 	}
1340 #endif
1341 
1342 	add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]);
1343 	t = CWDG(realpath_cache_ttl) ? 0 : -1;
1344 	path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL);
1345 
1346 	if (path_length < 0) {
1347 		errno = ENOENT;
1348 		return 1;
1349 	}
1350 
1351 	if (!start && !path_length) {
1352 		resolved_path[path_length++] = '.';
1353 	}
1354 	if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) {
1355 		if (path_length >= MAXPATHLEN-1) {
1356 			return -1;
1357 		}
1358 		resolved_path[path_length++] = DEFAULT_SLASH;
1359 	}
1360 	resolved_path[path_length] = 0;
1361 
1362 #ifdef ZEND_WIN32
1363 verify:
1364 #endif
1365 	if (verify_path) {
1366 		cwd_state old_state;
1367 
1368 		CWD_STATE_COPY(&old_state, state);
1369 		state->cwd_length = path_length;
1370 
1371 		tmp = erealloc(state->cwd, state->cwd_length+1);
1372 		state->cwd = (char *) tmp;
1373 
1374 		memcpy(state->cwd, resolved_path, state->cwd_length+1);
1375 		if (verify_path(state)) {
1376 			CWD_STATE_FREE(state);
1377 			*state = old_state;
1378 			ret = 1;
1379 		} else {
1380 			CWD_STATE_FREE(&old_state);
1381 			ret = 0;
1382 		}
1383 	} else {
1384 		state->cwd_length = path_length;
1385 		tmp = erealloc(state->cwd, state->cwd_length+1);
1386 		state->cwd = (char *) tmp;
1387 
1388 		memcpy(state->cwd, resolved_path, state->cwd_length+1);
1389 		ret = 0;
1390 	}
1391 
1392 #if VIRTUAL_CWD_DEBUG
1393 	fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
1394 #endif
1395 	return (ret);
1396 }
1397 /* }}} */
1398 
virtual_chdir(const char * path)1399 CWD_API int virtual_chdir(const char *path) /* {{{ */
1400 {
1401 	return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH)?-1:0;
1402 }
1403 /* }}} */
1404 
virtual_chdir_file(const char * path,int (* p_chdir)(const char * path))1405 CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path)) /* {{{ */
1406 {
1407 	int length = (int)strlen(path);
1408 	char *temp;
1409 	int retval;
1410 	ALLOCA_FLAG(use_heap)
1411 
1412 	if (length == 0) {
1413 		return 1; /* Can't cd to empty string */
1414 	}
1415 	while(--length >= 0 && !IS_SLASH(path[length])) {
1416 	}
1417 
1418 	if (length == -1) {
1419 		/* No directory only file name */
1420 		errno = ENOENT;
1421 		return -1;
1422 	}
1423 
1424 	if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
1425 		length++;
1426 	}
1427 	temp = (char *) do_alloca(length+1, use_heap);
1428 	memcpy(temp, path, length);
1429 	temp[length] = 0;
1430 #if VIRTUAL_CWD_DEBUG
1431 	fprintf (stderr, "Changing directory to %s\n", temp);
1432 #endif
1433 	retval = p_chdir(temp);
1434 	free_alloca(temp, use_heap);
1435 	return retval;
1436 }
1437 /* }}} */
1438 
virtual_realpath(const char * path,char * real_path)1439 CWD_API char *virtual_realpath(const char *path, char *real_path) /* {{{ */
1440 {
1441 	cwd_state new_state;
1442 	char *retval;
1443 	char cwd[MAXPATHLEN];
1444 
1445 	/* realpath("") returns CWD */
1446 	if (!*path) {
1447 		new_state.cwd = (char*)emalloc(1);
1448 		new_state.cwd[0] = '\0';
1449 		new_state.cwd_length = 0;
1450 		if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
1451 			path = cwd;
1452 		}
1453 	} else if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
1454 		CWD_STATE_COPY(&new_state, &CWDG(cwd));
1455 	} else {
1456 		new_state.cwd = (char*)emalloc(1);
1457 		new_state.cwd[0] = '\0';
1458 		new_state.cwd_length = 0;
1459 	}
1460 
1461 	if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)==0) {
1462 		int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;
1463 
1464 		memcpy(real_path, new_state.cwd, len);
1465 		real_path[len] = '\0';
1466 		retval = real_path;
1467 	} else {
1468 		retval = NULL;
1469 	}
1470 
1471 	CWD_STATE_FREE(&new_state);
1472 	return retval;
1473 }
1474 /* }}} */
1475 
virtual_filepath_ex(const char * path,char ** filepath,verify_path_func verify_path)1476 CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path) /* {{{ */
1477 {
1478 	cwd_state new_state;
1479 	int retval;
1480 
1481 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1482 	retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH);
1483 
1484 	*filepath = new_state.cwd;
1485 
1486 	return retval;
1487 
1488 }
1489 /* }}} */
1490 
virtual_filepath(const char * path,char ** filepath)1491 CWD_API int virtual_filepath(const char *path, char **filepath) /* {{{ */
1492 {
1493 	return virtual_filepath_ex(path, filepath, php_is_file_ok);
1494 }
1495 /* }}} */
1496 
virtual_fopen(const char * path,const char * mode)1497 CWD_API FILE *virtual_fopen(const char *path, const char *mode) /* {{{ */
1498 {
1499 	cwd_state new_state;
1500 	FILE *f;
1501 
1502 	if (path[0] == '\0') { /* Fail to open empty path */
1503 		return NULL;
1504 	}
1505 
1506 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1507 	if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1508 		CWD_STATE_FREE_ERR(&new_state);
1509 		return NULL;
1510 	}
1511 
1512 #ifdef ZEND_WIN32
1513 	f = php_win32_ioutil_fopen(new_state.cwd, mode);
1514 #else
1515 	f = fopen(new_state.cwd, mode);
1516 #endif
1517 
1518 	CWD_STATE_FREE_ERR(&new_state);
1519 
1520 	return f;
1521 }
1522 /* }}} */
1523 
virtual_access(const char * pathname,int mode)1524 CWD_API int virtual_access(const char *pathname, int mode) /* {{{ */
1525 {
1526 	cwd_state new_state;
1527 	int ret;
1528 
1529 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1530 	if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
1531 		CWD_STATE_FREE_ERR(&new_state);
1532 		return -1;
1533 	}
1534 
1535 #if defined(ZEND_WIN32)
1536 	ret = tsrm_win32_access(new_state.cwd, mode);
1537 #else
1538 	ret = access(new_state.cwd, mode);
1539 #endif
1540 
1541 	CWD_STATE_FREE_ERR(&new_state);
1542 
1543 	return ret;
1544 }
1545 /* }}} */
1546 
1547 #if HAVE_UTIME
virtual_utime(const char * filename,struct utimbuf * buf)1548 CWD_API int virtual_utime(const char *filename, struct utimbuf *buf) /* {{{ */
1549 {
1550 	cwd_state new_state;
1551 	int ret;
1552 
1553 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1554 	if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
1555 		CWD_STATE_FREE_ERR(&new_state);
1556 		return -1;
1557 	}
1558 
1559 #ifdef ZEND_WIN32
1560 	ret = win32_utime(new_state.cwd, buf);
1561 #else
1562 	ret = utime(new_state.cwd, buf);
1563 #endif
1564 
1565 	CWD_STATE_FREE_ERR(&new_state);
1566 	return ret;
1567 }
1568 /* }}} */
1569 #endif
1570 
virtual_chmod(const char * filename,mode_t mode)1571 CWD_API int virtual_chmod(const char *filename, mode_t mode) /* {{{ */
1572 {
1573 	cwd_state new_state;
1574 	int ret;
1575 
1576 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1577 	if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
1578 		CWD_STATE_FREE_ERR(&new_state);
1579 		return -1;
1580 	}
1581 
1582 #ifdef ZEND_WIN32
1583 	{
1584 		mode_t _tmp = mode;
1585 
1586 		mode = 0;
1587 
1588 		if (_tmp & _S_IREAD) {
1589 			mode |= _S_IREAD;
1590 		}
1591 		if (_tmp & _S_IWRITE) {
1592 			mode |= _S_IWRITE;
1593 		}
1594 		ret = php_win32_ioutil_chmod(new_state.cwd, mode);
1595 	}
1596 #else
1597 	ret = chmod(new_state.cwd, mode);
1598 #endif
1599 
1600 	CWD_STATE_FREE_ERR(&new_state);
1601 	return ret;
1602 }
1603 /* }}} */
1604 
1605 #if !defined(ZEND_WIN32)
virtual_chown(const char * filename,uid_t owner,gid_t group,int link)1606 CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link) /* {{{ */
1607 {
1608 	cwd_state new_state;
1609 	int ret;
1610 
1611 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1612 	if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
1613 		CWD_STATE_FREE_ERR(&new_state);
1614 		return -1;
1615 	}
1616 
1617 	if (link) {
1618 #if HAVE_LCHOWN
1619 		ret = lchown(new_state.cwd, owner, group);
1620 #else
1621 		ret = -1;
1622 #endif
1623 	} else {
1624 		ret = chown(new_state.cwd, owner, group);
1625 	}
1626 
1627 	CWD_STATE_FREE_ERR(&new_state);
1628 	return ret;
1629 }
1630 /* }}} */
1631 #endif
1632 
virtual_open(const char * path,int flags,...)1633 CWD_API int virtual_open(const char *path, int flags, ...) /* {{{ */
1634 {
1635 	cwd_state new_state;
1636 	int f;
1637 
1638 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1639 	if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
1640 		CWD_STATE_FREE_ERR(&new_state);
1641 		return -1;
1642 	}
1643 
1644 	if (flags & O_CREAT) {
1645 		mode_t mode;
1646 		va_list arg;
1647 
1648 		va_start(arg, flags);
1649 		mode = (mode_t) va_arg(arg, int);
1650 		va_end(arg);
1651 
1652 #ifdef ZEND_WIN32
1653 		f = php_win32_ioutil_open(new_state.cwd, flags, mode);
1654 #else
1655 		f = open(new_state.cwd, flags, mode);
1656 #endif
1657 	} else {
1658 #ifdef ZEND_WIN32
1659 		f = php_win32_ioutil_open(new_state.cwd, flags);
1660 #else
1661 		f = open(new_state.cwd, flags);
1662 #endif
1663 	}
1664 	CWD_STATE_FREE_ERR(&new_state);
1665 	return f;
1666 }
1667 /* }}} */
1668 
virtual_creat(const char * path,mode_t mode)1669 CWD_API int virtual_creat(const char *path, mode_t mode) /* {{{ */
1670 {
1671 	cwd_state new_state;
1672 	int f;
1673 
1674 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1675 	if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
1676 		CWD_STATE_FREE_ERR(&new_state);
1677 		return -1;
1678 	}
1679 
1680 	f = creat(new_state.cwd,  mode);
1681 
1682 	CWD_STATE_FREE_ERR(&new_state);
1683 	return f;
1684 }
1685 /* }}} */
1686 
virtual_rename(const char * oldname,const char * newname)1687 CWD_API int virtual_rename(const char *oldname, const char *newname) /* {{{ */
1688 {
1689 	cwd_state old_state;
1690 	cwd_state new_state;
1691 	int retval;
1692 
1693 	CWD_STATE_COPY(&old_state, &CWDG(cwd));
1694 	if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND)) {
1695 		CWD_STATE_FREE_ERR(&old_state);
1696 		return -1;
1697 	}
1698 	oldname = old_state.cwd;
1699 
1700 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1701 	if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND)) {
1702 		CWD_STATE_FREE_ERR(&old_state);
1703 		CWD_STATE_FREE_ERR(&new_state);
1704 		return -1;
1705 	}
1706 	newname = new_state.cwd;
1707 
1708 	/* rename on windows will fail if newname already exists.
1709 	   MoveFileEx has to be used */
1710 #ifdef ZEND_WIN32
1711 	/* MoveFileEx returns 0 on failure, other way 'round for this function */
1712 	retval = php_win32_ioutil_rename(oldname, newname);
1713 #else
1714 	retval = rename(oldname, newname);
1715 #endif
1716 
1717 	CWD_STATE_FREE_ERR(&old_state);
1718 	CWD_STATE_FREE_ERR(&new_state);
1719 
1720 	return retval;
1721 }
1722 /* }}} */
1723 
virtual_stat(const char * path,zend_stat_t * buf)1724 CWD_API int virtual_stat(const char *path, zend_stat_t *buf) /* {{{ */
1725 {
1726 	cwd_state new_state;
1727 	int retval;
1728 
1729 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1730 	if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1731 		CWD_STATE_FREE_ERR(&new_state);
1732 		return -1;
1733 	}
1734 
1735 	retval = php_sys_stat(new_state.cwd, buf);
1736 
1737 	CWD_STATE_FREE_ERR(&new_state);
1738 	return retval;
1739 }
1740 /* }}} */
1741 
virtual_lstat(const char * path,zend_stat_t * buf)1742 CWD_API int virtual_lstat(const char *path, zend_stat_t *buf) /* {{{ */
1743 {
1744 	cwd_state new_state;
1745 	int retval;
1746 
1747 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1748 	if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1749 		CWD_STATE_FREE_ERR(&new_state);
1750 		return -1;
1751 	}
1752 
1753 	retval = php_sys_lstat(new_state.cwd, buf);
1754 
1755 	CWD_STATE_FREE_ERR(&new_state);
1756 	return retval;
1757 }
1758 /* }}} */
1759 
virtual_unlink(const char * path)1760 CWD_API int virtual_unlink(const char *path) /* {{{ */
1761 {
1762 	cwd_state new_state;
1763 	int retval;
1764 
1765 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1766 	if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1767 		CWD_STATE_FREE_ERR(&new_state);
1768 		return -1;
1769 	}
1770 
1771 #ifdef ZEND_WIN32
1772 	retval = php_win32_ioutil_unlink(new_state.cwd);
1773 #else
1774 	retval = unlink(new_state.cwd);
1775 #endif
1776 
1777 	CWD_STATE_FREE_ERR(&new_state);
1778 	return retval;
1779 }
1780 /* }}} */
1781 
virtual_mkdir(const char * pathname,mode_t mode)1782 CWD_API int virtual_mkdir(const char *pathname, mode_t mode) /* {{{ */
1783 {
1784 	cwd_state new_state;
1785 	int retval;
1786 
1787 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1788 	if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH)) {
1789 		CWD_STATE_FREE_ERR(&new_state);
1790 		return -1;
1791 	}
1792 
1793 #ifdef ZEND_WIN32
1794 	retval = php_win32_ioutil_mkdir(new_state.cwd, mode);
1795 #else
1796 	retval = mkdir(new_state.cwd, mode);
1797 #endif
1798 	CWD_STATE_FREE_ERR(&new_state);
1799 	return retval;
1800 }
1801 /* }}} */
1802 
virtual_rmdir(const char * pathname)1803 CWD_API int virtual_rmdir(const char *pathname) /* {{{ */
1804 {
1805 	cwd_state new_state;
1806 	int retval;
1807 
1808 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1809 	if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND)) {
1810 		CWD_STATE_FREE_ERR(&new_state);
1811 		return -1;
1812 	}
1813 
1814 #ifdef ZEND_WIN32
1815 	retval = php_win32_ioutil_rmdir(new_state.cwd);
1816 #else
1817 	retval = rmdir(new_state.cwd);
1818 #endif
1819 	CWD_STATE_FREE_ERR(&new_state);
1820 	return retval;
1821 }
1822 /* }}} */
1823 
1824 #ifdef ZEND_WIN32
1825 DIR *opendir(const char *name);
1826 #endif
1827 
virtual_opendir(const char * pathname)1828 CWD_API DIR *virtual_opendir(const char *pathname) /* {{{ */
1829 {
1830 	cwd_state new_state;
1831 	DIR *retval;
1832 
1833 	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1834 	if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
1835 		CWD_STATE_FREE_ERR(&new_state);
1836 		return NULL;
1837 	}
1838 
1839 	retval = opendir(new_state.cwd);
1840 
1841 	CWD_STATE_FREE_ERR(&new_state);
1842 	return retval;
1843 }
1844 /* }}} */
1845 
1846 #ifdef ZEND_WIN32
virtual_popen(const char * command,const char * type)1847 CWD_API FILE *virtual_popen(const char *command, const char *type) /* {{{ */
1848 {
1849 	return popen_ex(command, type, CWDG(cwd).cwd, NULL);
1850 }
1851 /* }}} */
1852 #else /* Unix */
virtual_popen(const char * command,const char * type)1853 CWD_API FILE *virtual_popen(const char *command, const char *type) /* {{{ */
1854 {
1855 	size_t command_length;
1856 	int dir_length, extra = 0;
1857 	char *command_line;
1858 	char *ptr, *dir;
1859 	FILE *retval;
1860 
1861 	command_length = strlen(command);
1862 
1863 	dir_length = CWDG(cwd).cwd_length;
1864 	dir = CWDG(cwd).cwd;
1865 	while (dir_length > 0) {
1866 		if (*dir == '\'') extra+=3;
1867 		dir++;
1868 		dir_length--;
1869 	}
1870 	dir_length = CWDG(cwd).cwd_length;
1871 	dir = CWDG(cwd).cwd;
1872 
1873 	ptr = command_line = (char *) emalloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
1874 	memcpy(ptr, "cd ", sizeof("cd ")-1);
1875 	ptr += sizeof("cd ")-1;
1876 
1877 	if (CWDG(cwd).cwd_length == 0) {
1878 		*ptr++ = DEFAULT_SLASH;
1879 	} else {
1880 		*ptr++ = '\'';
1881 		while (dir_length > 0) {
1882 			switch (*dir) {
1883 			case '\'':
1884 				*ptr++ = '\'';
1885 				*ptr++ = '\\';
1886 				*ptr++ = '\'';
1887 				/* fall-through */
1888 			default:
1889 				*ptr++ = *dir;
1890 			}
1891 			dir++;
1892 			dir_length--;
1893 		}
1894 		*ptr++ = '\'';
1895 	}
1896 
1897 	*ptr++ = ' ';
1898 	*ptr++ = ';';
1899 	*ptr++ = ' ';
1900 
1901 	memcpy(ptr, command, command_length+1);
1902 	retval = popen(command_line, type);
1903 
1904 	efree(command_line);
1905 	return retval;
1906 }
1907 /* }}} */
1908 #endif
1909 
tsrm_realpath(const char * path,char * real_path)1910 CWD_API char *tsrm_realpath(const char *path, char *real_path) /* {{{ */
1911 {
1912 	cwd_state new_state;
1913 	char cwd[MAXPATHLEN];
1914 
1915 	/* realpath("") returns CWD */
1916 	if (!*path) {
1917 		new_state.cwd = (char*)emalloc(1);
1918 		new_state.cwd[0] = '\0';
1919 		new_state.cwd_length = 0;
1920 		if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
1921 			path = cwd;
1922 		}
1923 	} else if (!IS_ABSOLUTE_PATH(path, strlen(path)) &&
1924 					VCWD_GETCWD(cwd, MAXPATHLEN)) {
1925 		new_state.cwd = estrdup(cwd);
1926 		new_state.cwd_length = (int)strlen(cwd);
1927 	} else {
1928 		new_state.cwd = (char*)emalloc(1);
1929 		new_state.cwd[0] = '\0';
1930 		new_state.cwd_length = 0;
1931 	}
1932 
1933 	if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
1934 		efree(new_state.cwd);
1935 		return NULL;
1936 	}
1937 
1938 	if (real_path) {
1939 		int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length;
1940 		memcpy(real_path, new_state.cwd, copy_len);
1941 		real_path[copy_len] = '\0';
1942 		efree(new_state.cwd);
1943 		return real_path;
1944 	} else {
1945 		return new_state.cwd;
1946 	}
1947 }
1948 /* }}} */
1949 
1950 /*
1951  * Local variables:
1952  * tab-width: 4
1953  * c-basic-offset: 4
1954  * End:
1955  */
1956