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