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