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