diff --git a/NEWS b/NEWS index a833bf4355e..24947599456 100644 --- a/NEWS +++ b/NEWS @@ -40,6 +40,10 @@ PHP NEWS . Fixed bug #49169 (SoapServer calls wrong function, although "SOAP action" header is correct). (nielsdos) +- Windows: + . Fixed bug GH-10992 (Improper long path support for relative paths). (cmb, + nielsdos) + - XMLWriter: . Improved performance and reduce memory consumption. (nielsdos) diff --git a/ext/standard/tests/directory/gh10992.phpt b/ext/standard/tests/directory/gh10992.phpt new file mode 100644 index 00000000000..99ac8a5ecd2 --- /dev/null +++ b/ext/standard/tests/directory/gh10992.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-10992 (Improper long path support for relative paths) +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/win32/ioutil.c b/win32/ioutil.c index acd4103c613..9c313d6001b 100644 --- a/win32/ioutil.c +++ b/win32/ioutil.c @@ -281,7 +281,7 @@ PW32IO int php_win32_ioutil_close(int fd) PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode) {/*{{{*/ - size_t path_len; + size_t path_len, dir_len = 0; const wchar_t *my_path; if (!path) { @@ -292,7 +292,16 @@ PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode) PHP_WIN32_IOUTIL_CHECK_PATH_W(path, -1, 0) path_len = wcslen(path); - if (path_len < _MAX_PATH && path_len >= _MAX_PATH - 12) { +#ifndef ZTS + if (!PHP_WIN32_IOUTIL_IS_ABSOLUTEW(path, path_len) && !PHP_WIN32_IOUTIL_IS_JUNCTION_PATHW(path, path_len) && !PHP_WIN32_IOUTIL_IS_UNC_PATHW(path, path_len)) { + dir_len = GetCurrentDirectoryW(0, NULL); + if (dir_len == 0) { + return -1; + } + } +#endif + + if (dir_len + path_len < _MAX_PATH && dir_len + path_len >= _MAX_PATH - 12) { /* Special case here. From the doc: "When using an API to create a directory, the specified path cannot be @@ -315,7 +324,7 @@ PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode) } if (!PHP_WIN32_IOUTIL_IS_LONG_PATHW(tmp, path_len)) { - wchar_t *_tmp = (wchar_t *) malloc((path_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t)); + wchar_t *_tmp = (wchar_t *) malloc((dir_len + path_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t)); wchar_t *src, *dst; if (!_tmp) { SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY); @@ -325,6 +334,18 @@ PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode) memmove(_tmp, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t)); src = tmp; dst = _tmp + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW; +#ifndef ZTS + if (dir_len > 0) { + size_t len = GetCurrentDirectoryW(dir_len, dst); + if (len == 0 || len + 1 != dir_len) { + free(tmp); + free(_tmp); + return -1; + } + dst += len; + *dst++ = PHP_WIN32_IOUTIL_DEFAULT_SLASHW; + } +#endif while (src < tmp + path_len) { if (*src == PHP_WIN32_IOUTIL_FW_SLASHW) { *dst++ = PHP_WIN32_IOUTIL_DEFAULT_SLASHW; @@ -333,7 +354,7 @@ PW32IO int php_win32_ioutil_mkdir_w(const wchar_t *path, mode_t mode) *dst++ = *src++; } } - path_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW; + path_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + dir_len; _tmp[path_len] = L'\0'; free(tmp); tmp = _tmp; diff --git a/win32/ioutil.h b/win32/ioutil.h index 6a7055fc71a..454efdc3536 100644 --- a/win32/ioutil.h +++ b/win32/ioutil.h @@ -175,18 +175,28 @@ PW32IO php_win32_ioutil_normalization_result php_win32_ioutil_normalize_path_w(w __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, size_t in_len, size_t *out_len) {/*{{{*/ wchar_t *mb, *ret; - size_t mb_len; + size_t mb_len, dir_len = 0; mb = php_win32_cp_conv_any_to_w(in, in_len, &mb_len); if (!mb) { return NULL; } +#ifndef ZTS + if (!PHP_WIN32_IOUTIL_IS_ABSOLUTEW(mb, mb_len) && !PHP_WIN32_IOUTIL_IS_JUNCTION_PATHW(mb, mb_len) && !PHP_WIN32_IOUTIL_IS_UNC_PATHW(mb, mb_len)) { + dir_len = GetCurrentDirectoryW(0, NULL); + if (dir_len == 0) { + free(mb); + return NULL; + } + } +#endif + /* Only prefix with long if it's needed. */ - if (mb_len >= _MAX_PATH) { + if (dir_len + mb_len >= _MAX_PATH) { size_t new_mb_len; - ret = (wchar_t *) malloc((mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t)); + ret = (wchar_t *) malloc((dir_len + mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + 1) * sizeof(wchar_t)); if (!ret) { free(mb); return NULL; @@ -199,7 +209,7 @@ __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, siz } if (new_mb_len > mb_len) { - wchar_t *tmp = (wchar_t *) realloc(ret, (new_mb_len + 1) * sizeof(wchar_t)); + wchar_t *tmp = (wchar_t *) realloc(ret, (dir_len + new_mb_len + 1) * sizeof(wchar_t)); if (!tmp) { free(ret); free(mb); @@ -215,6 +225,18 @@ __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, siz } else { wchar_t *src = mb, *dst = ret + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW; memmove(ret, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW, PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW * sizeof(wchar_t)); +#ifndef ZTS + if (dir_len > 0) { + size_t len = GetCurrentDirectoryW(dir_len, dst); + if (len == 0 || len + 1 != dir_len) { + free(ret); + free(mb); + return NULL; + } + dst += len; + *dst++ = PHP_WIN32_IOUTIL_DEFAULT_SLASHW; + } +#endif while (src < mb + mb_len) { if (*src == PHP_WIN32_IOUTIL_FW_SLASHW) { *dst++ = PHP_WIN32_IOUTIL_DEFAULT_SLASHW; @@ -223,9 +245,9 @@ __forceinline static wchar_t *php_win32_ioutil_conv_any_to_w(const char* in, siz *dst++ = *src++; } } - ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW] = L'\0'; + ret[mb_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + dir_len] = L'\0'; - mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW; + mb_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW + dir_len; } free(mb);