34#include "tsrm_win32.h"
35# ifndef IO_REPARSE_TAG_SYMLINK
36# define IO_REPARSE_TAG_SYMLINK 0xA000000C
39# ifndef IO_REPARSE_TAG_DEDUP
40# define IO_REPARSE_TAG_DEDUP 0x80000013
43# ifndef IO_REPARSE_TAG_CLOUD
44# define IO_REPARSE_TAG_CLOUD (0x9000001AL)
48#ifndef IO_REPARSE_TAG_CLOUD_MASK
49#define IO_REPARSE_TAG_CLOUD_MASK (0x0000F000L)
52#ifndef IO_REPARSE_TAG_ONEDRIVE
53#define IO_REPARSE_TAG_ONEDRIVE (0x80000021L)
56# ifndef IO_REPARSE_TAG_ACTIVISION_HSM
57# define IO_REPARSE_TAG_ACTIVISION_HSM (0x00000047L)
60# ifndef IO_REPARSE_TAG_PROJFS
61# define IO_REPARSE_TAG_PROJFS (0x9000001CL)
64# ifndef VOLUME_NAME_NT
65# define VOLUME_NAME_NT 0x2
68# ifndef VOLUME_NAME_DOS
69# define VOLUME_NAME_DOS 0x0
76#define VIRTUAL_CWD_DEBUG 0
81#if defined(ZEND_WIN32) && defined(ZTS)
86ts_rsrc_id cwd_globals_id;
87size_t cwd_globals_offset;
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);
107#define CWD_STATE_FREE(s) \
112# define CWD_STATE_FREE_ERR(state) do { \
113 DWORD last_error = GetLastError(); \
114 CWD_STATE_FREE(state); \
115 SetLastError(last_error); \
118# define CWD_STATE_FREE_ERR(state) CWD_STATE_FREE(state)
157 for (i = 0; i < max_entries; i++) {
181 free(main_cwd_state.cwd);
185 ZeroMemory(&
cwd,
sizeof(
cwd));
197 if (main_cwd_state.cwd_length >= 2 &&
cwd[1] ==
':') {
201 main_cwd_state.cwd = strdup(
cwd);
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);
214#if (defined(ZEND_WIN32)) && defined(ZTS)
215 cwd_mutex = tsrm_mutex_alloc();
225#if (defined(ZEND_WIN32)) && defined(ZTS)
226 tsrm_mutex_free(cwd_mutex);
229 free(main_cwd_state.cwd);
256 if (
state->cwd_length == 0) {
268 if (
state->cwd_length == 2 &&
state->cwd[
state->cwd_length-1] ==
':') {
271 *length =
state->cwd_length+1;
285 *length =
state->cwd_length;
301 if (length >
size-1) {
316static inline zend_ulong realpath_cache_key(
const char *path,
size_t path_len)
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;
328 e = bucket_key + bucket_key_len;
329 for (h =
Z_UL(2166136261); bucket_key < e;) {
333 if (bucket_key_start != path) {
334 HeapFree(GetProcessHeap(), 0, (LPVOID)bucket_key_start);
340static inline zend_ulong realpath_cache_key(
const char *path,
size_t path_len)
343 const char *e = path + path_len;
345 for (h =
Z_UL(2166136261); path < e;) {
367 while (*bucket !=
NULL) {
368 if (
key == (*bucket)->key && path_len == (*bucket)->path_len &&
369 memcmp(path, (*bucket)->path, path_len) == 0) {
371 *bucket = (*bucket)->
next;
383 bucket = &(*bucket)->
next;
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)
394 if (realpath_len != path_len ||
395 memcmp(path,
realpath, path_len) != 0) {
396 size += realpath_len + 1;
404 if (bucket ==
NULL) {
408 bucket->
key = realpath_cache_key(path, path_len);
421 bucket->is_rvalid = 0;
422 bucket->is_readable = 0;
423 bucket->is_wvalid = 0;
424 bucket->is_writable = 0;
427 n = bucket->
key % (
sizeof(
CWDG(realpath_cache)) /
sizeof(
CWDG(realpath_cache)[0]));
429 CWDG(realpath_cache)[
n] = bucket;
441 while (*bucket !=
NULL) {
442 if (
CWDG(realpath_cache_ttl) && (*bucket)->expires < t) {
444 *bucket = (*bucket)->
next;
453 }
else if (
key == (*bucket)->key && path_len == (*bucket)->path_len &&
454 memcmp(path, (*bucket)->path, path_len) == 0) {
457 bucket = &(*bucket)->
next;
466 return realpath_cache_find(path, path_len, t);
477 return (
sizeof(
CWDG(realpath_cache)) /
sizeof(
CWDG(realpath_cache)[0]));
482 return CWDG(realpath_cache);
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)
492 int directory = 0, save;
494 WIN32_FIND_DATAW dataw;
495 HANDLE hFind = INVALID_HANDLE_VALUE;
497 wchar_t *pathw =
NULL;
498 int may_retry_reparse_point;
499#define FREE_PATHW() \
500 do { free(pathw); } while(0);
524 (i + 1 ==
len && path[i] ==
'.')) {
529 }
else if (i + 2 ==
len && path[i] ==
'.' && path[i+1] ==
'.') {
535 if (i <=
start + 1) {
538 j = tsrm_realpath_r(path,
start, i-1, ll, t, use_realpath, 1,
NULL);
539 if (
j >
start &&
j != (
size_t)-1) {
548 if (
j == 0 && path[0] ==
'.' && path[1] ==
'.' &&
555 path[
j+1] ==
'.' && path[
j+2] ==
'.' &&
563 }
else if (!
start && !
j) {
577 if (
start && save &&
CWDG(realpath_cache_size_limit)) {
582 if ((bucket = realpath_cache_find(path,
len, *t)) !=
NULL) {
588 *link_is_dir = bucket->
is_dir;
598 may_retry_reparse_point = 0;
605 hFind = FindFirstFileExW(pathw, FindExInfoBasic, &dataw, FindExSearchNameMatch,
NULL, 0);
606 if (INVALID_HANDLE_VALUE == hFind) {
622retry_reparse_tag_cloud:
624 !(IS_UNC_PATH(path,
len) &&
len >= 3 && path[2] !=
'?') &&
625 (dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
632 uint8_t isabsolute = 0;
633 wchar_t * reparsetarget;
636 char *printname =
NULL;
638 char *substitutename =
NULL;
639 size_t substitutename_len;
640 size_t substitutename_off = 0;
649 hLink = CreateFileW(pathw,
654 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS,
656 if(hLink == INVALID_HANDLE_VALUE) {
663 if (pbuffer ==
NULL) {
669 if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT,
NULL, 0, pbuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength,
NULL)) {
670 BY_HANDLE_FILE_INFORMATION fileInformation;
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;
683 goto retry_reparse_tag_cloud;
693 if(pbuffer->
ReparseTag == IO_REPARSE_TAG_SYMLINK) {
694 may_retry_reparse_point = 1;
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) {
720 free(substitutename);
728 else if(pbuffer->
ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
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) {
755 free(substitutename);
763 else if (pbuffer->
ReparseTag == IO_REPARSE_TAG_DEDUP ||
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) {
770 substitutename = malloc((
len + 1) *
sizeof(
char));
771 if (!substitutename) {
778 substitutename_len =
len;
787 if(isabsolute && substitutename_len > 4) {
795 if (
strncmp(substitutename,
"\\??\\Volume{",11) == 0
796 ||
strncmp(substitutename,
"\\\\?\\Volume{",11) == 0
797 ||
strncmp(substitutename,
"\\??\\UNC\\", 8) == 0
800 substitutename_off = 0;
803 if (
strncmp(substitutename,
"\\??\\", 4) == 0
804 ||
strncmp(substitutename,
"\\\\?\\", 4) == 0) {
805 substitutename_off = 4;
810 char * tmp2 = substitutename + substitutename_off;
811 for (bufindex = 0; bufindex + substitutename_off < substitutename_len; bufindex++) {
812 *(path + bufindex) = *(tmp2 + bufindex);
815 *(path + bufindex) = 0;
823 fprintf(stderr,
"reparse: print: %s ", printname);
824 fprintf(stderr,
"sub: %s ", substitutename);
825 fprintf(stderr,
"resolved: %s ", path);
829 free(substitutename);
831 if (may_retry_reparse_point) {
839 attrs = GetFileAttributesW(pathw);
840 if (!isVolume && attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_REPARSE_POINT)) {
843 goto retry_reparse_point;
847 if(isabsolute == 1) {
848 if (!((
j == 3) && (path[1] ==
':') && (path[2] ==
'\\'))) {
850 j = tsrm_realpath_r(path, 0,
j, ll, t, 0,
is_dir, &directory);
851 if(
j == (
size_t)-1) {
868 j = tsrm_realpath_r(path,
start, i +
j, ll, t, use_realpath,
is_dir, &directory);
869 if(
j == (
size_t)-1) {
875 directory = (dataw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
878 *link_is_dir = directory;
883 directory = (dataw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
884 if (
is_dir && !directory) {
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) {
912 j = tsrm_realpath_r(path, 1,
j, ll, t, use_realpath,
is_dir, &directory);
913 if (
j == (
size_t)-1) {
925 j = tsrm_realpath_r(path,
start, i +
j, ll, t, use_realpath,
is_dir, &directory);
926 if (
j == (
size_t)-1) {
932 *link_is_dir = directory;
936 directory =
S_ISDIR(st.st_mode);
938 *link_is_dir = directory;
940 if (
is_dir && !directory) {
947 if (i <=
start + 1) {
952 if (
j >
start &&
j != (
size_t)-1) {
990 if (save &&
start &&
CWDG(realpath_cache_size_limit)) {
992 realpath_cache_add(tmp,
len, path,
j, directory, *t);
1009 size_t path_length =
strlen(path);
1018 if (!path_length || path_length >=
MAXPATHLEN-1) {
1027#if VIRTUAL_CWD_DEBUG
1028 fprintf(stderr,
"cwd = %s path = %s\n",
state->cwd, path);
1035 if (
state->cwd_length == 0) {
1038 memcpy(resolved_path , path, path_length + 1);
1040 size_t state_cwd_length =
state->cwd_length;
1044 if (
state->cwd[1] ==
':') {
1046 state_cwd_length = 2;
1047 }
else if (IS_UNC_PATH(
state->cwd,
state->cwd_length)) {
1049 state_cwd_length = 2;
1053 while (
state->cwd[state_cwd_length] &&
1060 while (
state->cwd[state_cwd_length] &&
1067 if (path_length + state_cwd_length + 1 >=
MAXPATHLEN-1) {
1075 memcpy(resolved_path,
state->cwd, state_cwd_length);
1077 memcpy(resolved_path + state_cwd_length, path, path_length + 1);
1078 path_length += state_cwd_length;
1081 memcpy(resolved_path + state_cwd_length + 1, path, path_length + 1);
1082 path_length += state_cwd_length + 1;
1087 if (path_length > 2 && path[1] ==
':' && !
IS_SLASH(path[2])) {
1088 resolved_path[0] = path[0];
1089 resolved_path[1] =
':';
1091 memcpy(resolved_path + 3, path + 2, path_length - 1);
1095 memcpy(resolved_path, path, path_length + 1);
1099 if (memchr(resolved_path,
'*', path_length) ||
1100 memchr(resolved_path,
'?', path_length)) {
1107 if (IS_UNC_PATH(resolved_path, path_length)) {
1113 if (resolved_path[
start] == 0) {
1116 resolved_path[
start] = toupper(resolved_path[
start]);
1121 if (resolved_path[
start] == 0) {
1124 resolved_path[
start] = toupper(resolved_path[
start]);
1130 resolved_path[0] = toupper(resolved_path[0]);
1132 if (path_length == 2) {
1133 resolved_path[3] =
'\0';
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);
1143 if (path_length == (
size_t)-1) {
1145 if (
errno != EACCES) {
1154 if (!
start && !path_length) {
1155 resolved_path[path_length++] =
'.';
1158 if (add_slash && path_length && !
IS_SLASH(resolved_path[path_length-1])) {
1164 resolved_path[path_length] = 0;
1173 state->cwd_length = path_length;
1176 state->cwd = (
char *) tmp;
1179 if (verify_path(
state)) {
1188 state->cwd_length = path_length;
1190 state->cwd = (
char *) tmp;
1196#if VIRTUAL_CWD_DEBUG
1197 fprintf (stderr,
"virtual_file_ex() = %s\n",
state->cwd);
1213 size_t length =
strlen(path);
1233 temp = (
char *)
do_alloca(length+1, use_heap);
1234 memcpy(temp, path, length);
1236#if VIRTUAL_CWD_DEBUG
1237 fprintf (stderr,
"Changing directory to %s\n", temp);
1254 new_state.
cwd[0] =
'\0';
1263 new_state.
cwd[0] =
'\0';
1271 real_path[
len] =
'\0';
1291 *filepath = new_state.
cwd;
1310 if (path[0] ==
'\0') {
1321 f = php_win32_ioutil_fopen(new_state.
cwd,
mode);
1343#if defined(ZEND_WIN32)
1344 ret = tsrm_win32_access(new_state.
cwd,
mode);
1356CWD_API int virtual_utime(
const char *filename,
struct utimbuf *
buf)
1396 if (_tmp & _S_IREAD) {
1399 if (_tmp & _S_IWRITE) {
1402 ret = php_win32_ioutil_chmod(new_state.
cwd,
mode);
1413#if !defined(ZEND_WIN32)
1452 if (
flags & O_CREAT) {
1467 f = php_win32_ioutil_open(new_state.
cwd,
flags);
1488 f = creat(new_state.
cwd,
mode);
1506 oldname = old_state.
cwd;
1514 newname = new_state.
cwd;
1520 retval = php_win32_ioutil_rename(oldname, newname);
1580 retval = php_win32_ioutil_unlink(new_state.
cwd);
1623 retval = php_win32_ioutil_rmdir(new_state.
cwd);
1663 size_t command_length;
1664 int dir_length, extra = 0;
1669 command_length =
strlen(command);
1671 dir_length =
CWDG(
cwd).cwd_length;
1673 while (dir_length > 0) {
1674 if (*
dir ==
'\'') extra+=3;
1678 dir_length =
CWDG(
cwd).cwd_length;
1681 ptr = command_line = (
char *)
emalloc(command_length +
sizeof(
"cd '' ; ") + dir_length + extra+1+1);
1682 ptr = zend_mempcpy(
ptr,
"cd ",
sizeof(
"cd ") - 1);
1684 if (
CWDG(
cwd).cwd_length == 0) {
1688 while (dir_length > 0) {
1711 efree(command_line);
1725 new_state.
cwd[0] =
'\0';
1736 new_state.
cwd[0] =
'\0';
1747 memcpy(real_path, new_state.
cwd, copy_len);
1748 real_path[copy_len] =
'\0';
1752 return new_state.
cwd;
unlink(string $filename, $context=null)
fprintf($stream, string $format, mixed ... $values)
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)
lchown(string $filename, string|int $user)
#define PHP_WIN32_CP_IGNORE_LEN
memset(ptr, 0, type->size)
zend_ffi_ctype_name_buf buf
#define php_win32_ioutil_w_to_any
#define PHP_WIN32_IOUTIL_CHECK_PATH_W(pathw, ret, dealloc)
#define PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE
#define php_win32_ioutil_any_to_w(in)
#define php_win32_ioutil_conv_w_to_any
unsigned char key[REFLECTION_KEY_LEN]
unsigned short SubstituteNameOffset
struct PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER::@075116064036122104134040077213306127064052270261::@332270134025003203317152004221326066051376202346 SymbolicLinkReparseBuffer
unsigned short SubstituteNameLength
struct PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER::@075116064036122104134040077213306127064052270261::@322264331303223015276004312373161151004310266064 MountPointReparseBuffer
unsigned short PrintNameOffset
struct _realpath_cache_bucket * next
realpath_cache_bucket * realpath_cache[1024]
zend_long realpath_cache_size
zend_long realpath_cache_size_limit
zend_long realpath_cache_ttl
#define SET_ERRNO_FROM_WIN32_CODE(err)
#define erealloc(ptr, size)
strncmp(string $string1, string $string2, int $length)
#define ALLOCA_FLAG(name)
#define EXPECTED(condition)
#define do_alloca(p, use_heap)
#define free_alloca(p, use_heap)
ZEND_RESULT_CODE zend_result
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)
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 COPY_WHEN_ABSOLUTE(path)
#define IS_ABSOLUTE_PATH(path, len)
int(* verify_path_func)(const cwd_state *)
#define REALPATH_CACHE_SIZE
#define VCWD_GETCWD(buff, size)
struct _cwd_state cwd_state
struct _virtual_cwd_globals virtual_cwd_globals
#define REALPATH_CACHE_TTL