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