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