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