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