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