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. virtual_utime
  35. virtual_chmod
  36. virtual_chown
  37. virtual_open
  38. virtual_creat
  39. virtual_rename
  40. virtual_stat
  41. virtual_lstat
  42. virtual_unlink
  43. virtual_mkdir
  44. virtual_rmdir
  45. virtual_opendir
  46. virtual_popen
  47. virtual_popen
  48. virtual_popen
  49. tsrm_realpath

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

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