php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_virtual_cwd.c
Go to the documentation of this file.
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)
82MUTEX_T cwd_mutex;
83#endif
84
85#ifdef ZTS
86ts_rsrc_id cwd_globals_id;
87size_t cwd_globals_offset;
88#else
90#endif
91
92static 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
121static 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
132static 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
143static 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;
149 memset(cwd_g->realpath_cache, 0, sizeof(cwd_g->realpath_cache));
150}
151/* }}} */
152
153static 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) {
161 p = p->next;
162 free(r);
163 }
164 cache[i] = NULL;
165 }
166 *cache_size = 0;
167}
168
169static 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
175void 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
205CWD_API void virtual_cwd_startup(void) /* {{{ */
206{
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
220CWD_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
233CWD_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
241CWD_API void virtual_cwd_deactivate(void) /* {{{ */
242{
243 if (CWDG(cwd).cwd != NULL) {
245 CWDG(cwd).cwd = NULL;
246 }
247}
248/* }}} */
249
250CWD_API char *virtual_getcwd_ex(size_t *length) /* {{{ */
251{
253
254 state = &CWDG(cwd);
255
256 if (state->cwd_length == 0) {
257 char *retval;
258
259 *length = 1;
260 retval = (char *) emalloc(2);
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() */
291CWD_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
316static 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
340static 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
355CWD_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
361CWD_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) {
376 } else {
378 }
379
380 free(r);
381 return;
382 } else {
383 bucket = &(*bucket)->next;
384 }
385 }
386}
387/* }}} */
388
389static 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);
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;
431 }
432}
433/* }}} */
434
435static 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) {
449 } else {
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
464CWD_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
474
476{
477 return (sizeof(CWDG(realpath_cache)) / sizeof(CWDG(realpath_cache)[0]));
478}
479
481{
482 return CWDG(realpath_cache);
483}
484
485
486#undef LINK_MAX
487#define LINK_MAX 32
488
489static 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
597retry_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
622retry_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;
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,
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) */
1007CWD_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
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
1167verify:
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)) {
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
1203CWD_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 */
1211CWD_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
1245CWD_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) */
1283CWD_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) */
1299CWD_API int virtual_filepath(const char *path, char **filepath) /* {{{ */
1300{
1301 return virtual_filepath_ex(path, filepath, php_is_file_ok);
1302}
1303/* }}} */
1304
1305CWD_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
1332CWD_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
1356CWD_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
1379CWD_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)
1414CWD_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
1441CWD_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
1477CWD_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
1495CWD_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
1532CWD_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
1550CWD_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
1568CWD_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
1590CWD_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
1611CWD_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
1633DIR *opendir(const char *name);
1634#endif
1635
1636CWD_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
1655CWD_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 */
1661CWD_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++ = '\'';
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
1717CWD_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)) &&
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/* }}} */
size_t len
Definition apprentice.c:174
unlink(string $filename, $context=null)
fprintf($stream, string $format, mixed ... $values)
is_dir(string $filename)
rename(string $from, string $to, $context=null)
chown(string $filename, string|int $user)
opendir(string $directory, $context=null)
rmdir(string $directory, $context=null)
dir(string $directory, $context=null)
popen(string $command, string $mode)
chmod(string $filename, int $permissions)
fopen(string $filename, string $mode, bool $use_include_path=false, $context=null)
mkdir(string $directory, int $permissions=0777, bool $recursive=false, $context=null)
assert(mixed $assertion, Throwable|string|null $description=null)
link(string $target, string $link)
realpath(string $path)
lchown(string $filename, string|int $user)
#define PHP_WIN32_CP_IGNORE_LEN
Definition codepage.h:30
#define DWORD
Definition exif.c:1762
zend_ffi_type * type
Definition ffi.c:3812
zend_long n
Definition ffi.c:4979
new_type size
Definition ffi.c:4365
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
zval * arg
Definition ffi.c:3975
memset(ptr, 0, type->size)
buf start
Definition ffi.c:4687
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
char * mode
#define SIZE_MAX
Definition funcs.c:51
#define TRUE
Definition gd_gd.c:7
#define FALSE
Definition gd_gd.c:8
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
again j
#define php_win32_ioutil_w_to_any
Definition ioutil.h:252
unsigned short mode_t
Definition ioutil.h:72
#define PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, ret, dealloc)
Definition ioutil.h:160
#define PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE
Definition ioutil.h:141
#define php_win32_ioutil_any_to_w(in)
Definition ioutil.h:247
#define php_win32_ioutil_conv_w_to_any
Definition ioutil.h:253
int BOOL
#define memmove(a, b, c)
char * cwd
time()
unsigned char key[REFLECTION_KEY_LEN]
struct DIR_W32 DIR
Definition readdir.h:41
p
Definition session.c:1105
struct PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER::@075116064036122104134040077213306127064052270261::@332270134025003203317152004221326066051376202346 SymbolicLinkReparseBuffer
struct PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER::@075116064036122104134040077213306127064052270261::@322264331303223015276004312373161151004310266064 MountPointReparseBuffer
struct _realpath_cache_bucket * next
realpath_cache_bucket * realpath_cache[1024]
zend_long realpath_cache_size_limit
#define ENAMETOOLONG
#define errno
#define SET_ERRNO_FROM_WIN32_CODE(err)
Definition winutil.h:47
#define efree(ptr)
Definition zend_alloc.h:155
#define estrdup(s)
Definition zend_alloc.h:164
#define erealloc(ptr, size)
Definition zend_alloc.h:159
#define emalloc(size)
Definition zend_alloc.h:151
strlen(string $string)
strncmp(string $string1, string $string2, int $length)
#define EG(v)
int32_t zend_long
Definition zend_long.h:42
uint32_t zend_ulong
Definition zend_long.h:43
#define Z_UL(i)
Definition zend_long.h:49
#define ALLOCA_FLAG(name)
#define ZEND_FALLTHROUGH
#define EXPECTED(condition)
#define do_alloca(p, use_heap)
#define free_alloca(p, use_heap)
struct stat zend_stat_t
Definition zend_stream.h:94
@ FAILURE
Definition zend_types.h:61
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
CWD_API void virtual_cwd_shutdown(void)
#define CWD_STATE_COPY(d, s)
CWD_API char * virtual_getcwd_ex(size_t *length)
#define CWD_STATE_FREE(s)
CWD_API int virtual_chmod(const char *filename, mode_t mode)
CWD_API int virtual_stat(const char *path, zend_stat_t *buf)
CWD_API void realpath_cache_del(const char *path, size_t path_len)
CWD_API void virtual_cwd_activate(void)
#define LINK_MAX
CWD_API realpath_cache_bucket * realpath_cache_lookup(const char *path, size_t path_len, time_t t)
CWD_API int virtual_chdir_file(const char *path, int(*p_chdir)(const char *path))
CWD_API int virtual_unlink(const char *path)
CWD_API int virtual_filepath(const char *path, char **filepath)
void virtual_cwd_main_cwd_init(uint8_t reinit)
CWD_API int virtual_open(const char *path, int flags,...)
CWD_API zend_long realpath_cache_max_buckets(void)
CWD_API int virtual_access(const char *pathname, int mode)
CWD_API zend_result virtual_chdir(const char *path)
CWD_API FILE * virtual_fopen(const char *path, const char *mode)
virtual_cwd_globals cwd_globals
CWD_API void virtual_cwd_deactivate(void)
CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path)
CWD_API int virtual_mkdir(const char *pathname, mode_t mode)
CWD_API int virtual_rmdir(const char *pathname)
CWD_API int virtual_rename(const char *oldname, const char *newname)
CWD_API char * tsrm_realpath(const char *path, char *real_path)
CWD_API int virtual_creat(const char *path, mode_t mode)
CWD_API char * virtual_realpath(const char *path, char *real_path)
CWD_API int virtual_lstat(const char *path, zend_stat_t *buf)
CWD_API realpath_cache_bucket ** realpath_cache_get_buckets(void)
CWD_API void virtual_cwd_startup(void)
#define CWD_STATE_FREE_ERR(state)
CWD_API char * virtual_getcwd(char *buf, size_t size)
CWD_API FILE * virtual_popen(const char *command, const char *type)
CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link)
CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath)
CWD_API void realpath_cache_clean(void)
CWD_API DIR * virtual_opendir(const char *pathname)
CWD_API zend_long realpath_cache_size(void)
struct _realpath_cache_bucket realpath_cache_bucket
#define IS_SLASH(c)
#define DEFAULT_SLASH
#define CWD_REALPATH
#define COPY_WHEN_ABSOLUTE(path)
#define S_ISDIR(mode)
#define IS_ABSOLUTE_PATH(path, len)
int(* verify_path_func)(const cwd_state *)
#define CWD_EXPAND
#define S_ISLNK(mode)
#define php_sys_lstat
#define php_sys_stat
#define S_ISREG(mode)
#define REALPATH_CACHE_SIZE
#define CWDG(v)
#define CWD_FILEPATH
#define VCWD_GETCWD(buff, size)
#define MAXPATHLEN
#define CWD_API
struct _cwd_state cwd_state
struct _virtual_cwd_globals virtual_cwd_globals
#define REALPATH_CACHE_TTL
zval retval
zend_string * name
bool result
zval * ret