File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / TSRM / tsrm_virtual_cwd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:47:52 2012 UTC (12 years, 4 months ago) by misho
Branches: php, MAIN
CVS tags: v5_3_10, HEAD
php

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>