root/Zend/zend_virtual_cwd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. php_check_dots
  2. FileTimeToUnixTime
  3. php_sys_readlink
  4. php_sys_stat_ex
  5. php_is_dir_ok
  6. php_is_file_ok
  7. cwd_globals_ctor
  8. cwd_globals_dtor
  9. virtual_cwd_startup
  10. virtual_cwd_shutdown
  11. virtual_cwd_activate
  12. virtual_cwd_deactivate
  13. virtual_getcwd_ex
  14. virtual_getcwd
  15. realpath_cache_key
  16. realpath_cache_key
  17. realpath_cache_clean
  18. realpath_cache_del
  19. realpath_cache_add
  20. realpath_cache_find
  21. realpath_cache_lookup
  22. realpath_cache_size
  23. realpath_cache_max_buckets
  24. realpath_cache_get_buckets
  25. tsrm_realpath_r
  26. virtual_file_ex
  27. virtual_chdir
  28. virtual_chdir_file
  29. virtual_realpath
  30. virtual_filepath_ex
  31. virtual_filepath
  32. virtual_fopen
  33. virtual_access
  34. UnixTimeToFileTime
  35. win32_utime
  36. virtual_utime
  37. virtual_chmod
  38. virtual_chown
  39. virtual_open
  40. virtual_creat
  41. virtual_rename
  42. virtual_stat
  43. virtual_lstat
  44. virtual_unlink
  45. virtual_mkdir
  46. virtual_rmdir
  47. virtual_opendir
  48. virtual_popen
  49. virtual_popen
  50. virtual_popen
  51. tsrm_realpath

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

/* [<][>][^][v][top][bottom][index][help] */