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