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