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