diff --git a/TSRM/tsrm_config.w32.h b/TSRM/tsrm_config.w32.h index a58d47517ce20..ab451411797c6 100644 --- a/TSRM/tsrm_config.w32.h +++ b/TSRM/tsrm_config.w32.h @@ -6,7 +6,6 @@ #define HAVE_UTIME 1 #define HAVE_ALLOCA 1 -#define HAVE_REALPATH 1 #include #include diff --git a/TSRM/tsrm_win32.c b/TSRM/tsrm_win32.c index 902a693cccb76..4de7acbfe3632 100644 --- a/TSRM/tsrm_win32.c +++ b/TSRM/tsrm_win32.c @@ -766,15 +766,6 @@ TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf) } }/*}}}*/ -TSRM_API char *realpath(char *orig_path, char *buffer) -{/*{{{*/ - int ret = GetFullPathName(orig_path, _MAX_PATH, buffer, NULL); - if(!ret || ret > _MAX_PATH) { - return NULL; - } - return buffer; -}/*}}}*/ - #if HAVE_UTIME static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */ { diff --git a/TSRM/tsrm_win32.h b/TSRM/tsrm_win32.h index b90791cd59934..3edef1dd58276 100644 --- a/TSRM/tsrm_win32.h +++ b/TSRM/tsrm_win32.h @@ -110,8 +110,6 @@ TSRM_API int shmget(key_t key, size_t size, int flags); TSRM_API void *shmat(int key, const void *shmaddr, int flags); TSRM_API int shmdt(const void *shmaddr); TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf); - -TSRM_API char *realpath(char *orig_path, char *buffer); #endif /* diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c index 32c71a6149b4e..a05d1c58ac14a 100644 --- a/Zend/zend_virtual_cwd.c +++ b/Zend/zend_virtual_cwd.c @@ -1209,12 +1209,59 @@ static size_t tsrm_realpath_r(char *path, size_t start, size_t len, int *ll, tim } /* }}} */ +#ifdef ZEND_WIN32 +static size_t tsrm_win32_realpath_quick(char *path, size_t len, time_t *t) /* {{{ */ +{ + char tmp_resolved_path[MAXPATHLEN]; + int tmp_resolved_path_len; + BY_HANDLE_FILE_INFORMATION info; + realpath_cache_bucket *bucket; + + if (!*t) { + *t = time(0); + } + + if (CWDG(realpath_cache_size_limit) && (bucket = realpath_cache_find(path, len, *t)) != NULL) { + memcpy(path, bucket->realpath, bucket->realpath_len + 1); + return bucket->realpath_len; + } + +#if 0 + if (!php_win32_ioutil_realpath_ex0(resolved_path, tmp_resolved_path, &info)) { + if (CWD_REALPATH == use_realpath) { + DWORD err = GetLastError(); + SET_ERRNO_FROM_WIN32_CODE(err); + return 1; + } else { + /* Fallback to expand only to retain BC. */ + path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL); + } + } else { +#endif + + if (!php_win32_ioutil_realpath_ex0(path, tmp_resolved_path, &info)) { + DWORD err = GetLastError(); + SET_ERRNO_FROM_WIN32_CODE(err); + return (size_t)-1; + } + + tmp_resolved_path_len = strlen(tmp_resolved_path); + if (CWDG(realpath_cache_size_limit)) { + realpath_cache_add(path, len, tmp_resolved_path, tmp_resolved_path_len, info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY, *t); + } + memmove(path, tmp_resolved_path, tmp_resolved_path_len + 1); + + return tmp_resolved_path_len; +} +/* }}} */ +#endif + /* Resolve path relatively to state and put the real path into state */ /* returns 0 for ok, 1 for error */ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath) /* {{{ */ { size_t path_length = strlen(path); - char resolved_path[MAXPATHLEN]; + char resolved_path[MAXPATHLEN] = {0}; size_t start = 1; int ll = 0; time_t t; @@ -1336,7 +1383,27 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func add_slash = (use_realpath != CWD_REALPATH) && path_length > 0 && IS_SLASH(resolved_path[path_length-1]); t = CWDG(realpath_cache_ttl) ? 0 : -1; +#ifdef ZEND_WIN32 + if (CWD_EXPAND != use_realpath) { + size_t tmp_len = tsrm_win32_realpath_quick(resolved_path, path_length, &t); + if ((size_t)-1 != tmp_len) { + path_length = tmp_len; + } else { + DWORD err = GetLastError(); + /* The access denied error can mean something completely else, + fallback to complicated way. */ + if (CWD_REALPATH == use_realpath && ERROR_ACCESS_DENIED != err) { + SET_ERRNO_FROM_WIN32_CODE(err); + return 1; + } + path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL); + } + } else { + path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL); + } +#else path_length = tsrm_realpath_r(resolved_path, start, path_length, &ll, &t, use_realpath, 0, NULL); +#endif if (path_length == (size_t)-1) { errno = ENOENT; @@ -1346,6 +1413,7 @@ CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func if (!start && !path_length) { resolved_path[path_length++] = '.'; } + if (add_slash && path_length && !IS_SLASH(resolved_path[path_length-1])) { if (path_length >= MAXPATHLEN-1) { return -1; diff --git a/ext/opcache/Optimizer/zend_func_info.c b/ext/opcache/Optimizer/zend_func_info.c index 8b4f12829978c..13f69d9e55650 100644 --- a/ext/opcache/Optimizer/zend_func_info.c +++ b/ext/opcache/Optimizer/zend_func_info.c @@ -28,6 +28,9 @@ #include "zend_call_graph.h" #include "zend_func_info.h" #include "zend_inference.h" +#ifdef _WIN32 +#include "win32/ioutil.h" +#endif typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index ed150534a55c3..846ffdbc667c5 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -40,6 +40,7 @@ #ifdef PHP_WIN32 #include "win32/php_win32_globals.h" #include "win32/time.h" +#include "win32/ioutil.h" #endif typedef struct yy_buffer_state *YY_BUFFER_STATE; diff --git a/ext/standard/file.c b/ext/standard/file.c index e88197d95f2bf..099508019a913 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -50,6 +50,7 @@ # include "win32/param.h" # include "win32/winutil.h" # include "win32/fnmatch.h" +# include "win32/ioutil.h" #else # if HAVE_SYS_PARAM_H # include diff --git a/ext/standard/tests/file/realpath_cache_win32.phpt b/ext/standard/tests/file/realpath_cache_win32.phpt index e74a6565a7aba..fa5f22c3b59a6 100644 --- a/ext/standard/tests/file/realpath_cache_win32.phpt +++ b/ext/standard/tests/file/realpath_cache_win32.phpt @@ -11,7 +11,7 @@ if (substr(PHP_OS, 0, 3) != 'WIN') { var_dump(realpath_cache_size()); $data = realpath_cache_get(); -var_dump($data[__DIR__]); +var_dump($data[__FILE__]); echo "Done\n"; ?> @@ -21,9 +21,9 @@ array(8) { ["key"]=> %s(%d%s) ["is_dir"]=> - bool(true) + bool(false) ["realpath"]=> - string(%d) "%sfile" + string(%d) "%sphp" ["expires"]=> int(%d) ["is_rvalid"]=> diff --git a/win32/ioutil.c b/win32/ioutil.c index 3f56a44acb6fc..11761ce9d9b8c 100644 --- a/win32/ioutil.c +++ b/win32/ioutil.c @@ -719,6 +719,95 @@ PW32IO FILE *php_win32_ioutil_fopen_w(const wchar_t *path, const wchar_t *mode) return ret; }/*}}}*/ +static size_t php_win32_ioutil_realpath_h(HANDLE *h, wchar_t **resolved) +{/*{{{*/ + wchar_t ret[PHP_WIN32_IOUTIL_MAXPATHLEN], *ret_real; + DWORD ret_len, ret_real_len; + + ret_len = GetFinalPathNameByHandleW(h, ret, PHP_WIN32_IOUTIL_MAXPATHLEN-1, FILE_NAME_NORMALIZED | VOLUME_NAME_DOS); + if (0 == ret_len) { + DWORD err = GetLastError(); + SET_ERRNO_FROM_WIN32_CODE(err); + return (size_t)-1; + } else if (ret_len > PHP_WIN32_IOUTIL_MAXPATHLEN) { + SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER); + return (size_t)-1; + } + + if (NULL == *resolved) { + /* ret is expected to be either NULL or a buffer of capable size. */ + *resolved = (wchar_t *) malloc((ret_len + 1)*sizeof(wchar_t)); + if (!*resolved) { + SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY); + return (size_t)-1; + } + } + + ret_real = ret; + ret_real_len = ret_len; + if (0 == wcsncmp(ret, PHP_WIN32_IOUTIL_UNC_PATH_PREFIXW, PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW)) { + ret_real += (PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW - 2); + ret_real[0] = L'\\'; + ret_real_len -= (PHP_WIN32_IOUTIL_UNC_PATH_PREFIX_LENW - 2); + } else if (PHP_WIN32_IOUTIL_IS_LONG_PATHW(ret, ret_len)) { + ret_real += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW; + ret_real_len -= PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW; + } + memmove(*resolved, ret_real, (ret_real_len+1)*sizeof(wchar_t)); + + return ret_real_len; +}/*}}}*/ + +PW32IO wchar_t *php_win32_ioutil_realpath_w(const wchar_t *path, wchar_t *resolved) +{/*{{{*/ + return php_win32_ioutil_realpath_w_ex0(path, resolved, NULL); +}/*}}}*/ + +PW32IO wchar_t *php_win32_ioutil_realpath_w_ex0(const wchar_t *path, wchar_t *resolved, PBY_HANDLE_FILE_INFORMATION info) +{/*{{{*/ + HANDLE h; + size_t ret_len; + + PHP_WIN32_IOUTIL_CHECK_PATH_W(path, NULL, 0) + + h = CreateFileW(path, + 0, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (INVALID_HANDLE_VALUE == h) { + DWORD err = GetLastError(); + SET_ERRNO_FROM_WIN32_CODE(err); + return NULL; + } + + ret_len = php_win32_ioutil_realpath_h(h, &resolved); + if ((size_t)-1 == ret_len) { + DWORD err = GetLastError(); + CloseHandle(h); + SET_ERRNO_FROM_WIN32_CODE(err); + return NULL; + } + + if (NULL != info && !GetFileInformationByHandle(h, info)) { + DWORD err = GetLastError(); + CloseHandle(h); + SET_ERRNO_FROM_WIN32_CODE(err); + return NULL; + } + + CloseHandle(h); + + return resolved; +}/*}}}*/ + +PW32IO char *realpath(const char *path, char *resolved) +{/*{{{*/ + return php_win32_ioutil_realpath(path, resolved); +}/*}}}*/ + /* * Local variables: * tab-width: 4 diff --git a/win32/ioutil.h b/win32/ioutil.h index 488304d73d9a5..301566ddda8c0 100644 --- a/win32/ioutil.h +++ b/win32/ioutil.h @@ -258,6 +258,8 @@ PW32IO int php_win32_ioutil_unlink_w(const wchar_t *path); PW32IO int php_win32_ioutil_access_w(const wchar_t *path, mode_t mode); PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode); PW32IO FILE *php_win32_ioutil_fopen_w(const wchar_t *path, const wchar_t *mode); +PW32IO wchar_t *php_win32_ioutil_realpath_w(const wchar_t *path, wchar_t *resolved); +PW32IO wchar_t *php_win32_ioutil_realpath_w_ex0(const wchar_t *path, wchar_t *resolved, PBY_HANDLE_FILE_INFORMATION info); __forceinline static int php_win32_ioutil_access(const char *path, mode_t mode) {/*{{{*/ @@ -582,6 +584,59 @@ __forceinline static int php_win32_ioutil_mkdir(const char *path, mode_t mode) return ret; }/*}}}*/ +#define HAVE_REALPATH 1 +PW32IO char *realpath(const char *path, char *resolved); + +__forceinline static char *php_win32_ioutil_realpath_ex0(const char *path, char *resolved, PBY_HANDLE_FILE_INFORMATION info) +{/*{{{*/ + wchar_t retw[PHP_WIN32_IOUTIL_MAXPATHLEN]; + char *reta; + size_t reta_len; + + PHP_WIN32_IOUTIL_INIT_W(path) + if (!pathw) { + SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER); + return NULL; + } + + if (NULL == php_win32_ioutil_realpath_w_ex0(pathw, retw, info)) { + DWORD err = GetLastError(); + PHP_WIN32_IOUTIL_CLEANUP_W() + SET_ERRNO_FROM_WIN32_CODE(err); + return NULL; + } + + reta = php_win32_cp_conv_w_to_any(retw, PHP_WIN32_CP_IGNORE_LEN, &reta_len); + if (!reta || reta_len > PHP_WIN32_IOUTIL_MAXPATHLEN) { + DWORD err = GetLastError(); + PHP_WIN32_IOUTIL_CLEANUP_W() + SET_ERRNO_FROM_WIN32_CODE(err); + return NULL; + } + + if (NULL == resolved) { + /* ret is expected to be either NULL or a buffer of capable size. */ + resolved = (char *) malloc(reta_len + 1); + if (!resolved) { + free(reta); + PHP_WIN32_IOUTIL_CLEANUP_W() + SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + } + memmove(resolved, reta, reta_len+1); + + PHP_WIN32_IOUTIL_CLEANUP_W() + free(reta); + + return resolved; +}/*}}}*/ + +__forceinline static char *php_win32_ioutil_realpath(const char *path, char *resolved) +{/*{{{*/ + return php_win32_ioutil_realpath_ex0(path, resolved, NULL); +}/*}}}*/ + #ifdef __cplusplus } #endif