tsrm_virtual_cwd.c 39.2 KB
Newer Older
1 2 3 4
/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
5
   | Copyright (c) 1997-2009 The PHP Group                                |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 3.01 of the PHP license,      |
8 9
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
10
   | http://www.php.net/license/3_01.txt                                  |
11 12 13 14 15 16 17 18 19
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: Andi Gutmans <andi@zend.com>                                |
   |          Sascha Schumann <sascha@schumann.cx>                        |
   +----------------------------------------------------------------------+
*/

20
/* $Id: tsrm_virtual_cwd.c,v 1.74.2.9.2.35.2.22 2009/06/26 07:39:42 pajoye Exp $ */
21 22 23 24 25 26 27 28 29

#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
30
#include <time.h>
31 32 33 34 35 36 37

#include "tsrm_virtual_cwd.h"
#include "tsrm_strtok_r.h"

#ifdef TSRM_WIN32
#include <io.h>
#include "tsrm_win32.h"
38 39 40
# ifndef IO_REPARSE_TAG_SYMLINK
#  define IO_REPARSE_TAG_SYMLINK 0xA000000C
# endif
41 42 43
#endif

#ifdef NETWARE
44
#include <fsio.h>
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
#endif

#ifndef HAVE_REALPATH
#define realpath(x,y) strcpy(y,x)
#endif

#define VIRTUAL_CWD_DEBUG 0

#include "TSRM.h"

/* Only need mutex for popen() in Windows and NetWare because it doesn't chdir() on UNIX */
#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
MUTEX_T cwd_mutex;
#endif

#ifdef ZTS
61
ts_rsrc_id cwd_globals_id;
62
#else
63
virtual_cwd_globals cwd_globals;
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
#endif

cwd_state main_cwd_state; /* True global */

#ifndef TSRM_WIN32
#include <unistd.h>
#else
#include <direct.h>
#endif

#ifndef S_ISDIR
#define S_ISDIR(mode) ((mode) & _S_IFDIR)
#endif

#ifndef S_ISREG
#define S_ISREG(mode) ((mode) & _S_IFREG)
#endif

#ifdef TSRM_WIN32
#include <tchar.h>
#define tsrm_strtok_r(a,b,c) _tcstok((a),(b))
#define TOKENIZER_STRING "/\\"
	
static int php_check_dots(const char *element, int n) 
{
	while (n-- > 0) if (element[n] != '.') break;

	return (n != -1);
}
	
#define IS_DIRECTORY_UP(element, len) \
	(len >= 2 && !php_check_dots(element, len))

#define IS_DIRECTORY_CURRENT(element, len) \
	(len == 1 && element[0] == '.')

#elif defined(NETWARE)
/* NetWare has strtok() (in LibC) and allows both slashes in paths, like Windows --
   but rest of the stuff is like Unix */
/* strtok() call in LibC is abending when used in a different address space -- hence using
   PHP's version itself for now */
/*#define tsrm_strtok_r(a,b,c) strtok((a),(b))*/
#define TOKENIZER_STRING "/\\"

#else
#define TOKENIZER_STRING "/"
#endif


/* default macros */

#ifndef IS_DIRECTORY_UP
#define IS_DIRECTORY_UP(element, len) \
	(len == 2 && element[0] == '.' && element[1] == '.')
#endif

#ifndef IS_DIRECTORY_CURRENT
#define IS_DIRECTORY_CURRENT(element, len) \
	(len == 1 && element[0] == '.')
#endif

/* define this to check semantics */
#define IS_DIR_OK(s) (1)
	
#ifndef IS_DIR_OK
#define IS_DIR_OK(state) (php_is_dir_ok(state) == 0)
#endif


#define CWD_STATE_COPY(d, s)				\
	(d)->cwd_length = (s)->cwd_length;		\
	(d)->cwd = (char *) malloc((s)->cwd_length+1);	\
	memcpy((d)->cwd, (s)->cwd, (s)->cwd_length+1);

#define CWD_STATE_FREE(s)			\
	free((s)->cwd);
	
141
#ifdef TSRM_WIN32
142

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
#ifdef CTL_CODE
#undef CTL_CODE
#endif
#define CTL_CODE(DeviceType,Function,Method,Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define FILE_DEVICE_FILE_SYSTEM 0x00000009
#define METHOD_BUFFERED		0
#define FILE_ANY_ACCESS 	0
#define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE  ( 16 * 1024 )

typedef struct {
	unsigned long  ReparseTag;
	unsigned short ReparseDataLength;
	unsigned short Reserved;
	union {
		struct {
			unsigned short SubstituteNameOffset;
			unsigned short SubstituteNameLength;
			unsigned short PrintNameOffset;
			unsigned short PrintNameLength;
			unsigned long  Flags;
			wchar_t        ReparseTarget[1];
		} SymbolicLinkReparseBuffer;
		struct {
			unsigned short SubstituteNameOffset;
			unsigned short SubstituteNameLength;
			unsigned short PrintNameOffset;
			unsigned short PrintNameLength;
			wchar_t        ReparseTarget[1];
		} MountPointReparseBuffer;
		struct {
			unsigned char  ReparseTarget[1];
		} GenericReparseBuffer;
	};
} REPARSE_DATA_BUFFER;

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
#define SECS_BETWEEN_EPOCHS (__int64)11644473600
#define SECS_TO_100NS (__int64)10000000
static inline time_t FileTimeToUnixTime(const FILETIME FileTime)
{
	__int64 UnixTime;
	long *nsec = NULL;
	SYSTEMTIME SystemTime;
	FileTimeToSystemTime(&FileTime, &SystemTime);

	UnixTime = ((__int64)FileTime.dwHighDateTime << 32) +
	FileTime.dwLowDateTime;

	UnixTime -= (SECS_BETWEEN_EPOCHS * SECS_TO_100NS);

	if (nsec) {
		*nsec = (UnixTime % SECS_TO_100NS) * (__int64)100;
	}

	UnixTime /= SECS_TO_100NS; /* now convert to seconds */

	if ((time_t)UnixTime != UnixTime) {
		UnixTime = 0;
	}
	return (time_t)UnixTime;
}

205
CWD_API int php_sys_stat(const char *path, struct stat *buf) /* {{{ */
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
{
	WIN32_FILE_ATTRIBUTE_DATA data;
	__int64 t;

	if (!GetFileAttributesEx(path, GetFileExInfoStandard, &data)) {
		return stat(path, buf);
	}

	if (path[1] == ':') {
		if (path[0] >= 'A' && path[0] <= 'Z') {
			buf->st_dev = buf->st_rdev = path[0] - 'A';
		} else {
			buf->st_dev = buf->st_rdev = path[0] - 'a';
		}
	} else {
		char  cur_path[MAXPATHLEN+1];
		DWORD len = sizeof(cur_path);
		char *tmp = cur_path;

		while(1) {
			DWORD r = GetCurrentDirectory(len, tmp);
			if (r < len) {
				if (tmp[1] == ':') {
					if (path[0] >= 'A' && path[0] <= 'Z') {
						buf->st_dev = buf->st_rdev = path[0] - 'A';
					} else {
						buf->st_dev = buf->st_rdev = path[0] - 'a';
					}
				} else {
					buf->st_dev = buf->st_rdev = -1;
				}
				break;
			} else if (!r) {
				buf->st_dev = buf->st_rdev = -1;
				break;
			} else {
				len = r+1;
				tmp = (char*)malloc(len);
			}
		}
		if (tmp != cur_path) {
			free(tmp);
		}		    	
	}
	buf->st_uid = buf->st_gid = buf->st_ino = 0;
	buf->st_mode = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? (S_IFDIR|S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)) : S_IFREG;
	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));
	if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
		int len = strlen(path);

		if (path[len-4] == '.') {
			if (_memicmp(path+len-3, "exe", 3) == 0 ||
			    _memicmp(path+len-3, "com", 3) == 0 ||
			    _memicmp(path+len-3, "bat", 3) == 0 ||
			    _memicmp(path+len-3, "cmd", 3) == 0) {
				buf->st_mode  |= (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6));
			}
		}
264 265 266
	}

	buf->st_nlink = 1;
267 268 269 270
	t = data.nFileSizeHigh;
	t = t << 32;
	t |= data.nFileSizeLow;
	buf->st_size = t;
271 272 273 274 275 276
	buf->st_atime = FileTimeToUnixTime(data.ftLastAccessTime);
	buf->st_ctime = FileTimeToUnixTime(data.ftCreationTime);
	buf->st_mtime = FileTimeToUnixTime(data.ftLastWriteTime);
	if (buf->st_mtime != buf->st_atime) {
		buf->st_atime = buf->st_mtime;
	}
277 278
	return 0;
}
279
/* }}} */
280 281
#endif

282
static int php_is_dir_ok(const cwd_state *state)  /* {{{ */
283 284 285
{
	struct stat buf;

286
	if (php_sys_stat(state->cwd, &buf) == 0 && S_ISDIR(buf.st_mode))
287 288 289 290
		return (0);

	return (1);
}
291
/* }}} */
292

293
static int php_is_file_ok(const cwd_state *state)  /* {{{ */
294 295 296
{
	struct stat buf;

297
	if (php_sys_stat(state->cwd, &buf) == 0 && S_ISREG(buf.st_mode))
298 299 300 301
		return (0);

	return (1);
}
302
/* }}} */
303

304
static void cwd_globals_ctor(virtual_cwd_globals *cwd_globals TSRMLS_DC) /* {{{ */
305 306
{
	CWD_STATE_COPY(&cwd_globals->cwd, &main_cwd_state);
307 308 309 310
	cwd_globals->realpath_cache_size = 0;
	cwd_globals->realpath_cache_size_limit = REALPATH_CACHE_SIZE;
	cwd_globals->realpath_cache_ttl = REALPATH_CACHE_TTL;
	memset(cwd_globals->realpath_cache, 0, sizeof(cwd_globals->realpath_cache));
311
}
312
/* }}} */
313

314
static void cwd_globals_dtor(virtual_cwd_globals *cwd_globals TSRMLS_DC) /* {{{ */
315 316
{
	CWD_STATE_FREE(&cwd_globals->cwd);
317
	realpath_cache_clean(TSRMLS_C);
318
}
319
/* }}} */
320

321
CWD_API void virtual_cwd_startup(void) /* {{{ */
322 323 324 325
{
	char cwd[MAXPATHLEN];
	char *result;

326 327 328 329 330 331 332 333 334 335 336 337
#ifdef NETWARE
	result = getcwdpath(cwd, NULL, 1);
	if(result)
	{
		char *c=cwd;
		while(c = strchr(c, '\\'))
		{
			*c='/';
			++c;
		}
	}
#else
338
	result = getcwd(cwd, sizeof(cwd));	
339
#endif
340 341 342
	if (!result) {
		cwd[0] = '\0';
	}
343

344
	main_cwd_state.cwd_length = strlen(cwd);
345 346 347 348 349 350
#ifdef TSRM_WIN32
	if (main_cwd_state.cwd_length >= 2 && cwd[1] == ':') {
		cwd[0] = toupper(cwd[0]);
	}
#endif
	main_cwd_state.cwd = strdup(cwd);
351 352 353 354 355 356 357 358 359 360 361

#ifdef ZTS
	ts_allocate_id(&cwd_globals_id, sizeof(virtual_cwd_globals), (ts_allocate_ctor) cwd_globals_ctor, (ts_allocate_dtor) cwd_globals_dtor);
#else
	cwd_globals_ctor(&cwd_globals TSRMLS_CC);
#endif

#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
	cwd_mutex = tsrm_mutex_alloc();
#endif
}
362
/* }}} */
363

364
CWD_API void virtual_cwd_shutdown(void) /* {{{ */
365 366 367 368 369 370 371 372 373 374
{
#ifndef ZTS
	cwd_globals_dtor(&cwd_globals TSRMLS_CC);
#endif
#if (defined(TSRM_WIN32) || defined(NETWARE)) && defined(ZTS)
	tsrm_mutex_free(cwd_mutex);
#endif

	free(main_cwd_state.cwd); /* Don't use CWD_STATE_FREE because the non global states will probably use emalloc()/efree() */
}
375
/* }}} */
376

377
CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC) /* {{{ */
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
{
	cwd_state *state;

	state = &CWDG(cwd);

	if (state->cwd_length == 0) {
		char *retval;

		*length = 1;
		retval = (char *) malloc(2);
		retval[0] = DEFAULT_SLASH;
		retval[1] = '\0';	
		return retval;
	}

#ifdef TSRM_WIN32
	/* If we have something like C: */
	if (state->cwd_length == 2 && state->cwd[state->cwd_length-1] == ':') {
		char *retval;

		*length = state->cwd_length+1;
		retval = (char *) malloc(*length+1);
		memcpy(retval, state->cwd, *length);
401
		retval[0] = toupper(retval[0]);
402 403 404 405 406 407 408 409
		retval[*length-1] = DEFAULT_SLASH;
		retval[*length] = '\0';
		return retval;
	}
#endif
	*length = state->cwd_length;
	return strdup(state->cwd);
}
410
/* }}} */
411 412

/* Same semantics as UNIX getcwd() */
413
CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC) /* {{{ */
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
{
	size_t length;
	char *cwd;

	cwd = virtual_getcwd_ex(&length TSRMLS_CC);

	if (buf == NULL) {
		return cwd;
	}
	if (length > size-1) {
		free(cwd);
		errno = ERANGE; /* Is this OK? */
		return NULL;
	}
	memcpy(buf, cwd, length+1);
	free(cwd);
	return buf;
}
432
/* }}} */
433

434
static inline unsigned long realpath_cache_key(const char *path, int path_len) /* {{{ */
435
{
436 437 438 439 440 441 442 443 444 445
	register unsigned long h;
	const char *e = path + path_len;

	for (h = 2166136261U; path < e;) {
		h *= 16777619;
		h ^= *path++;
	}

	return h;
}
446
/* }}} */
447

448
CWD_API void realpath_cache_clean(TSRMLS_D) /* {{{ */
449 450 451 452 453 454 455 456 457 458 459 460 461 462
{
	int i;

	for (i = 0; i < sizeof(CWDG(realpath_cache))/sizeof(CWDG(realpath_cache)[0]); i++) {
		realpath_cache_bucket *p = CWDG(realpath_cache)[i];
		while (p != NULL) {
			realpath_cache_bucket *r = p;
			p = p->next;
			free(r);
		}
		CWDG(realpath_cache)[i] = NULL;
	}
	CWDG(realpath_cache_size) = 0;
}
463
/* }}} */
464

465
CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC) /* {{{ */
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
{
	unsigned long key = realpath_cache_key(path, path_len);
	unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
	realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];

	while (*bucket != NULL) {
		if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
		           memcmp(path, (*bucket)->path, path_len) == 0) {
			realpath_cache_bucket *r = *bucket;
			*bucket = (*bucket)->next;
			CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
			free(r);
			return;
		} else {
			bucket = &(*bucket)->next;
		}
	}
483
}
484
/* }}} */
485

486
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) /* {{{ */
487
{
488 489 490 491 492 493 494 495 496
	long size = sizeof(realpath_cache_bucket) + path_len + 1;
	int same = 1;
	
	if (realpath_len != path_len ||
	    memcmp(path, realpath, path_len) != 0) {
		size += realpath_len + 1;
		same = 0;
	}

497 498 499 500 501 502 503 504
	if (CWDG(realpath_cache_size) + size <= CWDG(realpath_cache_size_limit)) {
		realpath_cache_bucket *bucket = malloc(size);
		unsigned long n;
	
		bucket->key = realpath_cache_key(path, path_len);
		bucket->path = (char*)bucket + sizeof(realpath_cache_bucket);
		memcpy(bucket->path, path, path_len+1);
		bucket->path_len = path_len;
505 506 507 508 509 510
		if (same) {
			bucket->realpath = bucket->path;
		} else {
			bucket->realpath = bucket->path + (path_len + 1);
			memcpy(bucket->realpath, realpath, realpath_len+1);
		}
511
		bucket->realpath_len = realpath_len;
512
		bucket->is_dir = is_dir;
513 514 515 516 517 518
#ifdef PHP_WIN32
		bucket->is_rvalid   = 0;
		bucket->is_readable = 0;
		bucket->is_wvalid   = 0;
		bucket->is_writable = 0;
#endif
519 520 521 522
		bucket->expires = t + CWDG(realpath_cache_ttl);
		n = bucket->key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
		bucket->next = CWDG(realpath_cache)[n];
		CWDG(realpath_cache)[n] = bucket;
523
		CWDG(realpath_cache_size) += size;
524 525
	}
}
526
/* }}} */
527

528
static inline realpath_cache_bucket* realpath_cache_find(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
529 530 531 532 533 534 535 536
{
	unsigned long key = realpath_cache_key(path, path_len);
	unsigned long n = key % (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
	realpath_cache_bucket **bucket = &CWDG(realpath_cache)[n];

	while (*bucket != NULL) {
		if (CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
			realpath_cache_bucket *r = *bucket;
537 538 539
			*bucket = (*bucket)->next;
			CWDG(realpath_cache_size) -= sizeof(realpath_cache_bucket) + r->path_len + 1 + r->realpath_len + 1;
			free(r);
540 541 542 543
		} else if (key == (*bucket)->key && path_len == (*bucket)->path_len &&
		           memcmp(path, (*bucket)->path, path_len) == 0) {
			return *bucket;
		} else {
544
			bucket = &(*bucket)->next;
545 546 547 548
		}
	}
	return NULL;
}
549
/* }}} */
550

551 552 553 554 555 556
CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC) /* {{{ */
{
    return realpath_cache_find(path, path_len, t TSRMLS_CC);
}
/* }}} */

557 558 559 560
#undef LINK_MAX
#define LINK_MAX 32

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) /* {{{ */
561
{
562 563
	int i, j, save;
	int directory = 0;
564
#ifdef TSRM_WIN32
565 566 567 568
	WIN32_FIND_DATA data;
	HANDLE hFind;
#else
	struct stat st;
569
#endif
570 571
	realpath_cache_bucket *bucket;
	char *tmp;
572 573
	TSRM_ALLOCA_FLAG(use_heap)
	TSRM_ALLOCA_FLAG(use_heap_large)
574

575 576 577 578
	while (1) {
		if (len <= start) {
			return start;
		}
579

580 581 582 583
		i = len;
		while (i > start && !IS_SLASH(path[i-1])) {
			i--;
		}
584

585 586 587 588 589 590 591 592 593 594
		if (i == len ||
			(i == len - 1 && path[i] == '.')) {
			/* remove double slashes and '.' */
			len = i - 1;
			is_dir = 1;
			continue;
		} else if (i == len - 2 && path[i] == '.' && path[i+1] == '.') {
			/* remove '..' and previous directory */
			if (i - 1 <= start) {
				return start ? start : len;
595
			}
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
			j = tsrm_realpath_r(path, start, i-1, ll, t, use_realpath, 1, NULL TSRMLS_CC);
			if (j > start) {
				j--;
				while (j > start && !IS_SLASH(path[j])) {
					j--;
				}
				if (!start) {
					/* leading '..' must not be removed in case of relative path */
					if (j == 0 && path[0] == '.' && path[1] == '.' &&
					    IS_SLASH(path[2])) {
						path[3] = '.';
						path[4] = '.';
						path[5] = DEFAULT_SLASH;
						j = 5;
					} else if (j > 0 && 
				               path[j+1] == '.' && path[j+2] == '.' &&
				               IS_SLASH(path[j+3])) {
						j += 4;
						path[j++] = '.';
						path[j++] = '.';
						path[j] = DEFAULT_SLASH;
					}
				}
			} else if (!start && !j) {
				/* leading '..' must not be removed in case of relative path */
				path[0] = '.';
				path[1] = '.';
				path[2] = DEFAULT_SLASH;
				j = 2;
625
			}
626
			return j;
627
		}
628 629
	
		path[len] = 0;
630

631 632 633 634 635 636
		save = (use_realpath != CWD_EXPAND);

		if (start && save && CWDG(realpath_cache_size_limit)) {
			/* cache lookup for absolute path */
			if (!*t) {
				*t = time(0);
637
			}
638 639 640 641 642 643 644 645 646 647 648
		    if ((bucket = realpath_cache_find(path, len, *t TSRMLS_CC)) != NULL) {
		    	if (is_dir && !bucket->is_dir) {
					/* not a directory */
					return -1;
		    	} else {
		    		if (link_is_dir) {
		    			*link_is_dir = bucket->is_dir;
		    		}
					memcpy(path, bucket->realpath, bucket->realpath_len + 1);
				    return bucket->realpath_len;
				}
649
	    	}
650
		}
651

652 653
#ifdef TSRM_WIN32
		if (save && (hFind = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE) {
654
			if (use_realpath == CWD_REALPATH) {
655 656
				/* file not found */
				return -1;
657
			}
658 659
			/* continue resolution anyway but don't save result in the cache */
			save = 0;
660
		}
661
		
662
		if (save) {
663
			FindClose(hFind);
664
		}
665

666 667
		tmp = tsrm_do_alloca(len+1, use_heap);
		memcpy(tmp, path, len+1);
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757

		if(save && (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
			/* File is a reparse point. Get the target */
			HANDLE hLink = NULL;
			REPARSE_DATA_BUFFER * pbuffer;
			unsigned int retlength = 0, rname_off = 0;
			int bufindex = 0, rname_len = 0, isabsolute = 0;
			wchar_t * reparsetarget;

			if(++(*ll) > LINK_MAX) {
				return -1;
			}

			hLink = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
			if(hLink == INVALID_HANDLE_VALUE) {
				return -1;
			}

			pbuffer = (REPARSE_DATA_BUFFER *)tsrm_do_alloca(MAXIMUM_REPARSE_DATA_BUFFER_SIZE, use_heap_large);
			if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
				tsrm_free_alloca(pbuffer, use_heap_large);
				CloseHandle(hLink);
				return -1;
			}

			CloseHandle(hLink);

			if(pbuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
				rname_len = pbuffer->SymbolicLinkReparseBuffer.PrintNameLength/2;
				rname_off = pbuffer->SymbolicLinkReparseBuffer.PrintNameOffset/2;
				reparsetarget = pbuffer->SymbolicLinkReparseBuffer.ReparseTarget;
				isabsolute = (pbuffer->SymbolicLinkReparseBuffer.Flags == 0) ? 1 : 0;
			}
			else if(pbuffer->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
				rname_len = pbuffer->MountPointReparseBuffer.PrintNameLength/2;
				rname_off = pbuffer->MountPointReparseBuffer.PrintNameOffset/2;
				reparsetarget = pbuffer->MountPointReparseBuffer.ReparseTarget;
				isabsolute = 1;
			}
			else {
				tsrm_free_alloca(pbuffer, use_heap_large);
				return -1;
			}

			/* Convert wide string to narrow string */
			for(bufindex = 0; bufindex < rname_len; bufindex++) {
				*(path + bufindex) = (char)(reparsetarget[rname_off + bufindex]);
			}

			*(path + bufindex) = 0;
			tsrm_free_alloca(pbuffer, use_heap_large);
			j = bufindex;

			if(isabsolute == 1) {
				/* use_realpath is 0 in the call below coz path is absolute*/
				j = tsrm_realpath_r(path, 0, j, ll, t, 0, is_dir, &directory TSRMLS_CC);
				if(j < 0) {
					tsrm_free_alloca(tmp, use_heap);
					return -1;
				}
			}
			else {
				if(i + j >= MAXPATHLEN - 1) {
					tsrm_free_alloca(tmp, use_heap);
					return -1;
				}

				memmove(path+i, path, j+1);
				memcpy(path, tmp, i-1);
				path[i-1] = DEFAULT_SLASH;
				j  = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
				if(j < 0) {
					tsrm_free_alloca(tmp, use_heap);
					return -1;
				}
			}

			if(link_is_dir) {
				*link_is_dir = directory;
			}
		}
		else {
			if (save) {
				directory = (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
				if (is_dir && !directory) {
					/* not a directory */
					return -1;
				}
			}

758 759 760 761
#elif defined(NETWARE)
		save = 0;
		tmp = tsrm_do_alloca(len+1, use_heap);
		memcpy(tmp, path, len+1);
762
#else
763 764 765 766 767 768 769
		if (save && lstat(path, &st) < 0) {
			if (use_realpath == CWD_REALPATH) {
				/* file not found */
				return -1;
			}
			/* continue resolution anyway but don't save result in the cache */
			save = 0;
770 771
		}

772 773
		tmp = tsrm_do_alloca(len+1, use_heap);
		memcpy(tmp, path, len+1);
774

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
		if (save && S_ISLNK(st.st_mode)) {
			if (++(*ll) > LINK_MAX || (j = readlink(tmp, path, MAXPATHLEN)) < 0) {
			    /* too many links or broken symlinks */
				tsrm_free_alloca(tmp, use_heap);
				return -1;
			}
			path[j] = 0;
			if (IS_ABSOLUTE_PATH(path, j)) {
				j = tsrm_realpath_r(path, 1, j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
				if (j < 0) {
					tsrm_free_alloca(tmp, use_heap);
					return -1;
				}
			} else {
			    if (i + j >= MAXPATHLEN-1) {
					tsrm_free_alloca(tmp, use_heap);
					return -1; /* buffer overflow */
			    }
				memmove(path+i, path, j+1);
				memcpy(path, tmp, i-1);
				path[i-1] = DEFAULT_SLASH;
				j = tsrm_realpath_r(path, start, i + j, ll, t, use_realpath, is_dir, &directory TSRMLS_CC);
				if (j < 0) {
					tsrm_free_alloca(tmp, use_heap);
					return -1;
				}
			}
			if (link_is_dir) {
				*link_is_dir = directory;
			}
805
		} else {
806 807 808 809 810 811 812 813 814 815
			if (save) {
				directory = S_ISDIR(st.st_mode);
				if (link_is_dir) {
					*link_is_dir = directory;
				}
				if (is_dir && !directory) {
					/* not a directory */
					return -1;
				}
			}
816
#endif
817 818 819 820 821 822 823 824 825
			if (i - 1 <= start) {
				j = start;
			} else {
				/* some leading directories may be unaccessable */
				j = tsrm_realpath_r(path, start, i-1, ll, t, save ? CWD_FILEPATH : use_realpath, 1, NULL TSRMLS_CC);
				if (j > start) {
					path[j++] = DEFAULT_SLASH;
				}
			}
826
#ifdef TSRM_WIN32
827 828 829 830 831 832 833 834 835 836 837 838 839
			if (j < 0 || j + len - i >= MAXPATHLEN-1) {
				tsrm_free_alloca(tmp, use_heap);
				return -1;
			}
			if (save) {
				i = strlen(data.cFileName);
				memcpy(path+j, data.cFileName, i+1);
				j += i;
			} else {
				/* use the original file or directory name as it wasn't found */
				memcpy(path+j, tmp+i, len-i+1);
				j += (len-i);
			}
840
		}
841 842 843 844 845 846 847
#else
			if (j < 0 || j + len - i >= MAXPATHLEN-1) {
				tsrm_free_alloca(tmp, use_heap);
				return -1;
			}
			memcpy(path+j, tmp+i, len-i+1);
			j += (len-i);
848
		}
849
#endif
850

851 852 853 854
		if (save && start && CWDG(realpath_cache_size_limit)) {
			/* save absolute path in the cache */
			realpath_cache_add(tmp, len, path, j, directory, *t TSRMLS_CC);
		}
855

856 857 858 859 860
		tsrm_free_alloca(tmp, use_heap);
		return j;
	}
}
/* }}} */
861

862 863 864 865 866 867 868 869 870 871 872 873
/* Resolve path relatively to state and put the real path into state */
/* returns 0 for ok, 1 for error */
CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */
{
	int path_length = strlen(path);
	char resolved_path[MAXPATHLEN];
	int start = 1;
	int ll = 0;
	time_t t;
	int ret;
	int add_slash;
	TSRMLS_FETCH();
874

875 876 877 878 879 880
	if (path_length == 0 || path_length >= MAXPATHLEN-1) {
		return 1;
	}

#if VIRTUAL_CWD_DEBUG
	fprintf(stderr,"cwd = %s path = %s\n", state->cwd, path);
881
#endif
882 883 884 885 886 887 888 889 890 891 892

	/* cwd_length can be 0 when getcwd() fails.
	 * This can happen under solaris when a dir does not have read permissions
	 * but *does* have execute permissions */
	if (!IS_ABSOLUTE_PATH(path, path_length)) {
		if (state->cwd_length == 0) {
			/* resolve relative path */
			start = 0;
			memcpy(resolved_path , path, path_length + 1);
		} else {
			int state_cwd_length = state->cwd_length;
893 894

#ifdef TSRM_WIN32
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
			if (IS_SLASH(path[0])) {
				if (state->cwd[1] == ':') {
					/* Copy only the drive name */
					state_cwd_length = 2;
				} else if (IS_UNC_PATH(state->cwd, state->cwd_length)) {
					/* Copy only the share name */
					state_cwd_length = 2;
					while (IS_SLASH(state->cwd[state_cwd_length])) {
						state_cwd_length++;
					}						 
					while (state->cwd[state_cwd_length] &&
					       !IS_SLASH(state->cwd[state_cwd_length])) {
						state_cwd_length++;
					}						 
					while (IS_SLASH(state->cwd[state_cwd_length])) {
						state_cwd_length++;
					}						 
					while (state->cwd[state_cwd_length] &&
					       !IS_SLASH(state->cwd[state_cwd_length])) {
						state_cwd_length++;
					}						 
916
				}
917
			}
918
#endif
919 920
			if (path_length + state_cwd_length + 1 >= MAXPATHLEN-1) {
				return 1;
921
			}
922 923 924 925
			memcpy(resolved_path, state->cwd, state_cwd_length);
			resolved_path[state_cwd_length] = DEFAULT_SLASH;
			memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1);
			path_length += state_cwd_length + 1;
926
		}
927 928 929 930 931 932 933 934 935 936 937 938
	} else {		
#ifdef TSRM_WIN32
		if (path_length > 2 && path[1] == ':' && !IS_SLASH(path[2])) {
			resolved_path[0] = path[0];
			resolved_path[1] = ':';
			resolved_path[2] = DEFAULT_SLASH;
			memcpy(resolved_path + 3, path + 2, path_length - 1);
			path_length++;
		} else
#endif
		memcpy(resolved_path, path, path_length + 1);
	} 
939

940 941 942 943 944
#ifdef TSRM_WIN32
	if (memchr(resolved_path, '*', path_length) ||
	    memchr(resolved_path, '?', path_length)) {
		return 1;
	}
945
#endif
946 947 948 949 950 951 952 953 954 955

#ifdef TSRM_WIN32
	if (IS_UNC_PATH(resolved_path, path_length)) {
		/* skip UNC name */
		resolved_path[0] = DEFAULT_SLASH;
		resolved_path[1] = DEFAULT_SLASH;
		start = 2;
		while (!IS_SLASH(resolved_path[start])) {
			if (resolved_path[start] == 0) {
				goto verify;
956
			}
957 958
			resolved_path[start] = toupper(resolved_path[start]);
			start++;
959
		}
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
		resolved_path[start++] = DEFAULT_SLASH;
		while (!IS_SLASH(resolved_path[start])) {
			if (resolved_path[start] == 0) {
				goto verify;
			}
			resolved_path[start] = toupper(resolved_path[start]);
			start++;
		}
		resolved_path[start++] = DEFAULT_SLASH;
	} else if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
		/* skip DRIVE name */
		resolved_path[0] = toupper(resolved_path[0]);
		resolved_path[2] = DEFAULT_SLASH;
		start = 3;
	}
#elif defined(NETWARE)
	if (IS_ABSOLUTE_PATH(resolved_path, path_length)) {
		/* skip VOLUME name */
		start = 0;
		while (start != ':') {
			if (resolved_path[start] == 0) return -1;
			start++;
		}
		start++;
		if (!IS_SLASH(resolved_path[start])) return -1;
		resolved_path[start++] = DEFAULT_SLASH;
	}
#endif
988

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
	add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]);
	t = CWDG(realpath_cache_ttl) ? 0 : -1;
	path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL TSRMLS_CC);
	
	if (path_length < 0) {
		errno = ENOENT;
		return 1;
	}
	
	if (!start && !path_length) {
		resolved_path[path_length++] = '.';
	}
	if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) {
		if (path_length >= MAXPATHLEN-1) {
			return -1;
1004
		}
1005
		resolved_path[path_length++] = DEFAULT_SLASH;
1006
	}
1007
	resolved_path[path_length] = 0;
1008

1009
#ifdef TSRM_WIN32
1010
verify:
1011
#endif
1012 1013
	if (verify_path) {
		cwd_state old_state;
1014

1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
		CWD_STATE_COPY(&old_state, state);
		state->cwd_length = path_length;
		state->cwd = (char *) realloc(state->cwd, state->cwd_length+1);
		memcpy(state->cwd, resolved_path, state->cwd_length+1);
		if (verify_path(state)) {
			CWD_STATE_FREE(state);
			*state = old_state;
			ret = 1;
		} else {
			CWD_STATE_FREE(&old_state);
			ret = 0;
		}
1027
	} else {
1028 1029 1030
		state->cwd_length = path_length;
		state->cwd = (char *) realloc(state->cwd, state->cwd_length+1);
		memcpy(state->cwd, resolved_path, state->cwd_length+1);
1031 1032 1033 1034 1035 1036 1037 1038
		ret = 0;
	}
	
#if VIRTUAL_CWD_DEBUG
	fprintf (stderr, "virtual_file_ex() = %s\n",state->cwd);
#endif
	return (ret);
}
1039
/* }}} */
1040

1041
CWD_API int virtual_chdir(const char *path TSRMLS_DC) /* {{{ */
1042
{
1043
	return virtual_file_ex(&CWDG(cwd), path, php_is_dir_ok, CWD_REALPATH)?-1:0;
1044
}
1045
/* }}} */
1046

1047
CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC) /* {{{ */
1048 1049 1050 1051
{
	int length = strlen(path);
	char *temp;
	int retval;
1052
	TSRM_ALLOCA_FLAG(use_heap)
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068

	if (length == 0) {
		return 1; /* Can't cd to empty string */
	}	
	while(--length >= 0 && !IS_SLASH(path[length])) {
	}

	if (length == -1) {
		/* No directory only file name */
		errno = ENOENT;
		return -1;
	}

	if (length == COPY_WHEN_ABSOLUTE(path) && IS_ABSOLUTE_PATH(path, length+1)) { /* Also use trailing slash if this is absolute */
		length++;
	}
1069
	temp = (char *) tsrm_do_alloca(length+1, use_heap);
1070 1071 1072 1073 1074 1075
	memcpy(temp, path, length);
	temp[length] = 0;
#if VIRTUAL_CWD_DEBUG
	fprintf (stderr, "Changing directory to %s\n", temp);
#endif
	retval = p_chdir(temp TSRMLS_CC);
1076
	tsrm_free_alloca(temp, use_heap);
1077 1078
	return retval;
}
1079
/* }}} */
1080

1081
CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */
1082 1083 1084
{
	cwd_state new_state;
	char *retval;
1085
	char cwd[MAXPATHLEN];
1086

1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
	/* realpath("") returns CWD */
	if (!*path) {
		new_state.cwd = (char*)malloc(1);
		new_state.cwd[0] = '\0';
		new_state.cwd_length = 0;		
	    if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
		    path = cwd;
		}
	} else if (!IS_ABSOLUTE_PATH(path, strlen(path))) {
		CWD_STATE_COPY(&new_state, &CWDG(cwd));
	} else {
		new_state.cwd = (char*)malloc(1);
		new_state.cwd[0] = '\0';
		new_state.cwd_length = 0;		
	}
1102

1103
	if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)==0) {
1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
		int len = new_state.cwd_length>MAXPATHLEN-1?MAXPATHLEN-1:new_state.cwd_length;

		memcpy(real_path, new_state.cwd, len);
		real_path[len] = '\0';
		retval = real_path;
	} else {
		retval = NULL;
	}

	CWD_STATE_FREE(&new_state);

	return retval;
}
1117
/* }}} */
1118

1119
CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC) /* {{{ */
1120 1121 1122 1123 1124
{
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1125
	retval = virtual_file_ex(&new_state, path, verify_path, CWD_FILEPATH);
1126 1127 1128 1129 1130 1131

	*filepath = new_state.cwd;

	return retval;

}
1132
/* }}} */
1133

1134
CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC) /* {{{ */
1135 1136 1137
{
	return virtual_filepath_ex(path, filepath, php_is_file_ok TSRMLS_CC);
}
1138
/* }}} */
1139

1140
CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC) /* {{{ */
1141 1142 1143 1144 1145 1146 1147 1148 1149
{
	cwd_state new_state;
	FILE *f;

	if (path[0] == '\0') { /* Fail to open empty path */
		return NULL;
	}

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1150 1151
	if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
		CWD_STATE_FREE(&new_state);
1152 1153
		return NULL;
	}
1154 1155 1156 1157 1158 1159

	f = fopen(new_state.cwd, mode);

	CWD_STATE_FREE(&new_state);
	return f;
}
1160
/* }}} */
1161

1162
CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC) /* {{{ */
1163 1164 1165 1166 1167
{
	cwd_state new_state;
	int ret;
	
	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1168 1169
	if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
		CWD_STATE_FREE(&new_state);
1170 1171
		return -1;
	}
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

#if defined(TSRM_WIN32)
	ret = tsrm_win32_access(new_state.cwd, mode);
#else
	ret = access(new_state.cwd, mode);
#endif
	
	CWD_STATE_FREE(&new_state);
	
	return ret;
}
1183
/* }}} */
1184 1185

#if HAVE_UTIME
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
#ifdef TSRM_WIN32
static void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
{
	// Note that LONGLONG is a 64-bit value
	LONGLONG ll;

	ll = Int32x32To64(t, 10000000) + 116444736000000000;
	pft->dwLowDateTime = (DWORD)ll;
	pft->dwHighDateTime = ll >> 32;
}
/* }}} */

static int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
{
	FILETIME mtime, atime;
	HANDLE hFile; 

	hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
				 OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);

	/* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but 
	   the CreateFile operation succeeds */
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
		SetLastError(0);
	}

	if ( hFile == INVALID_HANDLE_VALUE ) {
		return -1;
	}

	if (!buf) {
		SYSTEMTIME st;
		GetSystemTime(&st);
		SystemTimeToFileTime(&st, &mtime);
		atime = mtime;
	} else {
		UnixTimeToFileTime(buf->modtime, &mtime);
		UnixTimeToFileTime(buf->actime, &atime);
	}
	if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
		CloseHandle(hFile);
		return -1;
	}
	CloseHandle(hFile);
	return 1;
}
/* }}} */
#endif

1235
CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC) /* {{{ */
1236 1237 1238 1239 1240
{
	cwd_state new_state;
	int ret;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1241 1242
	if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
		CWD_STATE_FREE(&new_state);
1243 1244
		return -1;
	}
1245

1246 1247 1248
#ifdef TSRM_WIN32
	ret = win32_utime(new_state.cwd, buf);
#else
1249
	ret = utime(new_state.cwd, buf);
1250
#endif
1251 1252 1253 1254

	CWD_STATE_FREE(&new_state);
	return ret;
}
1255
/* }}} */
1256 1257
#endif

1258
CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC) /* {{{ */
1259 1260 1261 1262 1263
{
	cwd_state new_state;
	int ret;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1264 1265
	if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
		CWD_STATE_FREE(&new_state);
1266 1267
		return -1;
	}
1268 1269 1270 1271 1272 1273

	ret = chmod(new_state.cwd, mode);

	CWD_STATE_FREE(&new_state);
	return ret;
}
1274
/* }}} */
1275 1276

#if !defined(TSRM_WIN32) && !defined(NETWARE)
1277
CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC) /* {{{ */
1278 1279 1280 1281 1282
{
	cwd_state new_state;
	int ret;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1283 1284
	if (virtual_file_ex(&new_state, filename, NULL, CWD_REALPATH)) {
		CWD_STATE_FREE(&new_state);
1285 1286
		return -1;
	}
1287

1288 1289 1290 1291 1292 1293 1294 1295 1296
	if (link) {
#if HAVE_LCHOWN
		ret = lchown(new_state.cwd, owner, group);
#else
		ret = -1;
#endif
	} else {
		ret = chown(new_state.cwd, owner, group);
	}
1297 1298 1299 1300

	CWD_STATE_FREE(&new_state);
	return ret;
}
1301
/* }}} */
1302 1303
#endif

1304
CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...) /* {{{ */
1305 1306 1307 1308 1309
{
	cwd_state new_state;
	int f;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1310 1311
	if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
		CWD_STATE_FREE(&new_state);
1312 1313
		return -1;
	}
1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329

	if (flags & O_CREAT) {
		mode_t mode;
		va_list arg;

		va_start(arg, flags);
		mode = (mode_t) va_arg(arg, int);
		va_end(arg);

		f = open(new_state.cwd, flags, mode);
	} else {
		f = open(new_state.cwd, flags);
	}	
	CWD_STATE_FREE(&new_state);
	return f;
}
1330
/* }}} */
1331

1332
CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC) /* {{{ */
1333 1334 1335 1336 1337
{
	cwd_state new_state;
	int f;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1338 1339
	if (virtual_file_ex(&new_state, path, NULL, CWD_FILEPATH)) {
		CWD_STATE_FREE(&new_state);
1340 1341
		return -1;
	}
1342 1343 1344 1345 1346 1347

	f = creat(new_state.cwd,  mode);

	CWD_STATE_FREE(&new_state);
	return f;
}
1348
/* }}} */
1349

1350
CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC) /* {{{ */
1351 1352 1353 1354 1355 1356
{
	cwd_state old_state;
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&old_state, &CWDG(cwd));
1357
	if (virtual_file_ex(&old_state, oldname, NULL, CWD_EXPAND)) {
1358
		CWD_STATE_FREE(&old_state);
1359 1360
		return -1;
	}
1361 1362 1363
	oldname = old_state.cwd;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1364
	if (virtual_file_ex(&new_state, newname, NULL, CWD_EXPAND)) {
1365 1366
		CWD_STATE_FREE(&old_state);
		CWD_STATE_FREE(&new_state);
1367 1368
		return -1;
	}
1369
	newname = new_state.cwd;
1370 1371 1372 1373 1374 1375

	/* rename on windows will fail if newname already exists.
	   MoveFileEx has to be used */
#ifdef TSRM_WIN32
	retval = (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING) == 0) ? -1 : 0;
#else
1376
	retval = rename(oldname, newname);
1377
#endif
1378 1379 1380 1381 1382 1383

	CWD_STATE_FREE(&old_state);
	CWD_STATE_FREE(&new_state);

	return retval;
}
1384
/* }}} */
1385

1386
CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */
1387 1388 1389 1390 1391
{
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1392 1393
	if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
		CWD_STATE_FREE(&new_state);
1394 1395
		return -1;
	}
1396

1397
	retval = php_sys_stat(new_state.cwd, buf);
1398 1399 1400 1401

	CWD_STATE_FREE(&new_state);
	return retval;
}
1402
/* }}} */
1403

1404
#if !defined(TSRM_WIN32)
1405
CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC) /* {{{ */
1406 1407 1408 1409 1410
{
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1411
	if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
1412
		CWD_STATE_FREE(&new_state);
1413 1414
		return -1;
	}
1415 1416 1417 1418 1419 1420

	retval = lstat(new_state.cwd, buf);

	CWD_STATE_FREE(&new_state);
	return retval;
}
1421
/* }}} */
1422 1423
#endif

1424
CWD_API int virtual_unlink(const char *path TSRMLS_DC) /* {{{ */
1425 1426 1427 1428 1429
{
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1430 1431
	if (virtual_file_ex(&new_state, path, NULL, CWD_EXPAND)) {
		CWD_STATE_FREE(&new_state);
1432 1433
		return -1;
	}
1434 1435 1436 1437 1438 1439

	retval = unlink(new_state.cwd);

	CWD_STATE_FREE(&new_state);
	return retval;
}
1440
/* }}} */
1441

1442
CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC) /* {{{ */
1443 1444 1445 1446 1447
{
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1448 1449
	if (virtual_file_ex(&new_state, pathname, NULL, CWD_FILEPATH)) {
		CWD_STATE_FREE(&new_state);
1450 1451
		return -1;
	}
1452 1453 1454 1455 1456 1457 1458 1459 1460

#ifdef TSRM_WIN32
	retval = mkdir(new_state.cwd);
#else
	retval = mkdir(new_state.cwd, mode);
#endif
	CWD_STATE_FREE(&new_state);
	return retval;
}
1461
/* }}} */
1462

1463
CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC) /* {{{ */
1464 1465 1466 1467 1468
{
	cwd_state new_state;
	int retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1469 1470
	if (virtual_file_ex(&new_state, pathname, NULL, CWD_EXPAND)) {
		CWD_STATE_FREE(&new_state);
1471 1472
		return -1;
	}
1473 1474 1475 1476 1477 1478

	retval = rmdir(new_state.cwd);

	CWD_STATE_FREE(&new_state);
	return retval;
}
1479
/* }}} */
1480 1481 1482 1483 1484

#ifdef TSRM_WIN32
DIR *opendir(const char *name);
#endif

1485
CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC) /* {{{ */
1486 1487 1488 1489 1490
{
	cwd_state new_state;
	DIR *retval;

	CWD_STATE_COPY(&new_state, &CWDG(cwd));
1491 1492
	if (virtual_file_ex(&new_state, pathname, NULL, CWD_REALPATH)) {
		CWD_STATE_FREE(&new_state);
1493 1494
		return NULL;
	}
1495 1496 1497 1498 1499 1500

	retval = opendir(new_state.cwd);

	CWD_STATE_FREE(&new_state);
	return retval;
}
1501
/* }}} */
1502 1503

#ifdef TSRM_WIN32
1504
CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
1505 1506 1507
{
	return popen_ex(command, type, CWDG(cwd).cwd, NULL);
}
1508
/* }}} */
1509 1510 1511 1512
#elif defined(NETWARE)
/* On NetWare, the trick of prepending "cd cwd; " doesn't work so we need to perform
   a VCWD_CHDIR() and mutex it
 */
1513
CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537
{
	char prev_cwd[MAXPATHLEN];
	char *getcwd_result;
	FILE *retval;

	getcwd_result = VCWD_GETCWD(prev_cwd, MAXPATHLEN);
	if (!getcwd_result) {
		return NULL;
	}

#ifdef ZTS
	tsrm_mutex_lock(cwd_mutex);
#endif

	VCWD_CHDIR(CWDG(cwd).cwd);
	retval = popen(command, type);
	VCWD_CHDIR(prev_cwd);

#ifdef ZTS
	tsrm_mutex_unlock(cwd_mutex);
#endif

	return retval;
}
1538
/* }}} */
1539
#else /* Unix */
1540
CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC) /* {{{ */
1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
{
	int command_length;
	int dir_length, extra = 0;
	char *command_line;
	char *ptr, *dir;
	FILE *retval;

	command_length = strlen(command);

	dir_length = CWDG(cwd).cwd_length;
	dir = CWDG(cwd).cwd;
	while (dir_length > 0) {
		if (*dir == '\'') extra+=3;
		dir++;
		dir_length--;
	}
	dir_length = CWDG(cwd).cwd_length;
	dir = CWDG(cwd).cwd;

1560
	ptr = command_line = (char *) malloc(command_length + sizeof("cd '' ; ") + dir_length + extra+1+1);
1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596
	if (!command_line) {
		return NULL;
	}
	memcpy(ptr, "cd ", sizeof("cd ")-1);
	ptr += sizeof("cd ")-1;

	if (CWDG(cwd).cwd_length == 0) {
		*ptr++ = DEFAULT_SLASH;
	} else {
		*ptr++ = '\'';
		while (dir_length > 0) {
			switch (*dir) {
			case '\'':
				*ptr++ = '\'';
				*ptr++ = '\\';
				*ptr++ = '\'';
				/* fall-through */
			default:
				*ptr++ = *dir;
			}
			dir++;
			dir_length--;
		}
		*ptr++ = '\'';
	}
	
	*ptr++ = ' ';
	*ptr++ = ';';
	*ptr++ = ' ';

	memcpy(ptr, command, command_length+1);
	retval = popen(command_line, type);

	free(command_line);
	return retval;
}
1597
/* }}} */
1598 1599
#endif

1600
CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC) /* {{{ */
1601
{
1602 1603
	cwd_state new_state;
	char cwd[MAXPATHLEN];
1604

1605 1606 1607 1608 1609 1610 1611 1612 1613
	/* realpath("") returns CWD */
	if (!*path) {
		new_state.cwd = (char*)malloc(1);
		new_state.cwd[0] = '\0';
		new_state.cwd_length = 0;		
	    if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
		    path = cwd;
		}
	} else if (!IS_ABSOLUTE_PATH(path, strlen(path)) &&
1614 1615 1616
	    VCWD_GETCWD(cwd, MAXPATHLEN)) {
		new_state.cwd = strdup(cwd);
		new_state.cwd_length = strlen(cwd);
1617
	} else {
1618 1619 1620
		new_state.cwd = (char*)malloc(1);
		new_state.cwd[0] = '\0';
		new_state.cwd_length = 0;		
1621 1622
	}

1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637
	if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) {
		free(new_state.cwd);
		return NULL;
	}

	if (real_path) {
		int copy_len = new_state.cwd_length>MAXPATHLEN-1 ? MAXPATHLEN-1 : new_state.cwd_length;
		memcpy(real_path, new_state.cwd, copy_len);
		real_path[copy_len] = '\0';
		free(new_state.cwd);
		return real_path;
	} else {
		return new_state.cwd;
	}
}
1638
/* }}} */
1639 1640 1641 1642 1643 1644 1645

/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 */