summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnatol Belski <ab@php.net>2018-10-03 18:56:08 +0200
committerAnatol Belski <ab@php.net>2018-10-03 18:56:55 +0200
commit91c905e83c338ef66da824be4f90c1d78d134507 (patch)
tree6095583ab22193f27d4837f53d1f4f5e5a5850d8
parent97481fccb9265df7e813f8c4e3f25d3a20651ce2 (diff)
downloadphp-git-91c905e83c338ef66da824be4f90c1d78d134507.tar.gz
Refactor php_sys_readlink
Also move the implementation into win32 where it belongs
-rw-r--r--Zend/zend_virtual_cwd.c72
-rw-r--r--Zend/zend_virtual_cwd.h2
-rw-r--r--win32/ftok.c1
-rw-r--r--win32/ioutil.c132
-rw-r--r--win32/ioutil.h36
5 files changed, 170 insertions, 73 deletions
diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c
index 079fab3eb6..16734501ec 100644
--- a/Zend/zend_virtual_cwd.c
+++ b/Zend/zend_virtual_cwd.c
@@ -113,78 +113,6 @@ static cwd_state main_cwd_state; /* True global */
# define CWD_STATE_FREE_ERR(state) CWD_STATE_FREE(state)
#endif
-#ifdef ZEND_WIN32
-CWD_API ssize_t php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */
- HANDLE hFile;
- wchar_t *linkw = php_win32_ioutil_any_to_w(link), targetw[MAXPATHLEN];
- size_t ret_len, targetw_len, offset = 0;
- char *ret;
-
- if (!linkw) {
- return -1;
- }
-
- if (!target_len) {
- free(linkw);
- return -1;
- }
-
- hFile = CreateFileW(linkw, // file to open
- 0, // query possible attributes
- PHP_WIN32_IOUTIL_DEFAULT_SHARE_MODE,
- NULL, // default security
- OPEN_EXISTING, // existing file only
- FILE_FLAG_BACKUP_SEMANTICS, // normal file
- NULL); // no attr. template
- if( hFile == INVALID_HANDLE_VALUE) {
- free(linkw);
- return -1;
- }
-
- /* Despite MSDN has documented it won't to, the length returned by
- GetFinalPathNameByHandleA includes the length of the
- null terminator. This behavior is at least reproducible
- with VS2012 and earlier, and seems not to be fixed till
- now. Thus, correcting target_len so it's suddenly don't
- overflown. */
- targetw_len = GetFinalPathNameByHandleW(hFile, targetw, MAXPATHLEN, VOLUME_NAME_DOS);
- if(targetw_len >= target_len || targetw_len >= MAXPATHLEN || targetw_len == 0) {
- free(linkw);
- CloseHandle(hFile);
- return -1;
- }
-
- if(targetw_len > 4) {
- /* Skip first 4 characters if they are "\\?\" */
- if(targetw[0] == L'\\' && targetw[1] == L'\\' && targetw[2] == L'?' && targetw[3] == L'\\') {
- offset = 4;
-
- /* \\?\UNC\ */
- if (targetw_len > 7 && targetw[4] == L'U' && targetw[5] == L'N' && targetw[6] == L'C') {
- offset += 2;
- targetw[offset] = L'\\';
- }
- }
- }
-
- ret = php_win32_ioutil_conv_w_to_any(targetw + offset, targetw_len - offset, &ret_len);
- if (!ret || ret_len >= MAXPATHLEN) {
- CloseHandle(hFile);
- free(linkw);
- free(ret);
- return -1;
- }
- memcpy(target, ret, ret_len + 1);
-
- free(ret);
- CloseHandle(hFile);
- free(linkw);
-
- return (ssize_t)ret_len;
-}
-/* }}} */
-#endif
-
static int php_is_dir_ok(const cwd_state *state) /* {{{ */
{
zend_stat_t buf;
diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h
index 9773ef1e48..afee6ee7d4 100644
--- a/Zend/zend_virtual_cwd.h
+++ b/Zend/zend_virtual_cwd.h
@@ -118,7 +118,7 @@ typedef unsigned short mode_t;
# define php_sys_stat php_win32_ioutil_stat
# define php_sys_lstat php_win32_ioutil_lstat
# define php_sys_fstat php_win32_ioutil_fstat
-CWD_API ssize_t php_sys_readlink(const char *link, char *target, size_t target_len);
+# define php_sys_readlink php_win32_ioutil_readlink
# define php_sys_symlink php_win32_ioutil_symlink
# define php_sys_link php_win32_ioutil_link
#else
diff --git a/win32/ftok.c b/win32/ftok.c
index 469f2df59a..77de6a0449 100644
--- a/win32/ftok.c
+++ b/win32/ftok.c
@@ -16,6 +16,7 @@
+----------------------------------------------------------------------+
*/
+#include "php.h"
#include "ipc.h"
#include <windows.h>
diff --git a/win32/ioutil.c b/win32/ioutil.c
index 99eccd511f..0ca01261a5 100644
--- a/win32/ioutil.c
+++ b/win32/ioutil.c
@@ -982,6 +982,138 @@ PW32IO int php_win32_ioutil_fstat(int fd, php_win32_ioutil_stat_t *buf)
return php_win32_ioutil_fstat_int((HANDLE)_get_osfhandle(fd), buf, NULL, 0, NULL);
}/*}}}*/
+static ssize_t php_win32_ioutil_readlink_int(HANDLE h, wchar_t *buf, size_t buf_len)
+{/*{{{*/
+ char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER *reparse_data = (PHP_WIN32_IOUTIL_REPARSE_DATA_BUFFER*) buffer;
+ wchar_t* reparse_target;
+ DWORD reparse_target_len;
+ DWORD bytes;
+
+ if (!DeviceIoControl(h,
+ FSCTL_GET_REPARSE_POINT,
+ NULL,
+ 0,
+ buffer,
+ sizeof buffer,
+ &bytes,
+ NULL)) {
+ SET_ERRNO_FROM_WIN32_CODE(GetLastError());
+ return -1;
+ }
+
+ if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
+ /* Real symlink */
+ reparse_target = reparse_data->SymbolicLinkReparseBuffer.ReparseTarget +
+ (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset /
+ sizeof(wchar_t));
+ reparse_target_len =
+ reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength /
+ sizeof(wchar_t);
+
+ /* Real symlinks can contain pretty much everything, but the only thing we
+ * really care about is undoing the implicit conversion to an NT namespaced
+ * path that CreateSymbolicLink will perform on absolute paths. If the path
+ * is win32-namespaced then the user must have explicitly made it so, and
+ * we better just return the unmodified reparse data. */
+ if (reparse_target_len >= 4 &&
+ reparse_target[0] == L'\\' &&
+ reparse_target[1] == L'?' &&
+ reparse_target[2] == L'?' &&
+ reparse_target[3] == L'\\') {
+ /* Starts with \??\ */
+ if (reparse_target_len >= 6 &&
+ ((reparse_target[4] >= L'A' && reparse_target[4] <= L'Z') ||
+ (reparse_target[4] >= L'a' && reparse_target[4] <= L'z')) &&
+ reparse_target[5] == L':' &&
+ (reparse_target_len == 6 || reparse_target[6] == L'\\')) {
+ /* \??\<drive>:\ */
+ reparse_target += 4;
+ reparse_target_len -= 4;
+
+ } else if (reparse_target_len >= 8 &&
+ (reparse_target[4] == L'U' || reparse_target[4] == L'u') &&
+ (reparse_target[5] == L'N' || reparse_target[5] == L'n') &&
+ (reparse_target[6] == L'C' || reparse_target[6] == L'c') &&
+ reparse_target[7] == L'\\') {
+ /* \??\UNC\<server>\<share>\ - make sure the final path looks like
+ * \\<server>\<share>\ */
+ reparse_target += 6;
+ reparse_target[0] = L'\\';
+ reparse_target_len -= 6;
+ }
+ }
+
+ } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
+ /* Junction. */
+ reparse_target = reparse_data->MountPointReparseBuffer.ReparseTarget +
+ (reparse_data->MountPointReparseBuffer.SubstituteNameOffset /
+ sizeof(wchar_t));
+ reparse_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength / sizeof(wchar_t);
+
+ /* Only treat junctions that look like \??\<drive>:\ as symlink. Junctions
+ * can also be used as mount points, like \??\Volume{<guid>}, but that's
+ * confusing for programs since they wouldn't be able to actually
+ * understand such a path when returned by uv_readlink(). UNC paths are
+ * never valid for junctions so we don't care about them. */
+ if (!(reparse_target_len >= 6 &&
+ reparse_target[0] == L'\\' &&
+ reparse_target[1] == L'?' &&
+ reparse_target[2] == L'?' &&
+ reparse_target[3] == L'\\' &&
+ ((reparse_target[4] >= L'A' && reparse_target[4] <= L'Z') ||
+ (reparse_target[4] >= L'a' && reparse_target[4] <= L'z')) &&
+ reparse_target[5] == L':' &&
+ (reparse_target_len == 6 || reparse_target[6] == L'\\'))) {
+ SET_ERRNO_FROM_WIN32_CODE(ERROR_SYMLINK_NOT_SUPPORTED);
+ return -1;
+ }
+
+ /* Remove leading \??\ */
+ reparse_target += 4;
+ reparse_target_len -= 4;
+
+ } else {
+ /* Reparse tag does not indicate a symlink. */
+ SET_ERRNO_FROM_WIN32_CODE(ERROR_SYMLINK_NOT_SUPPORTED);
+ return -1;
+ }
+
+ if (reparse_target_len >= buf_len) {
+ SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+
+ memcpy(buf, reparse_target, (reparse_target_len + 1)*sizeof(wchar_t));
+
+ return reparse_target_len;
+}/*}}}*/
+
+PW32IO ssize_t php_win32_ioutil_readlink_w(const wchar_t *path, wchar_t *buf, size_t buf_len)
+{/*{{{*/
+ HANDLE h;
+ ssize_t ret;
+
+ h = CreateFileW(path,
+ 0,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (h == INVALID_HANDLE_VALUE) {
+ SET_ERRNO_FROM_WIN32_CODE(GetLastError());
+ return -1;
+ }
+
+ ret = php_win32_ioutil_readlink_int(h, buf, buf_len);
+
+ CloseHandle(h);
+
+ return ret;
+}/*}}}*/
+
/*
* Local variables:
* tab-width: 4
diff --git a/win32/ioutil.h b/win32/ioutil.h
index 9da518dbc4..db21c449e0 100644
--- a/win32/ioutil.h
+++ b/win32/ioutil.h
@@ -751,6 +751,42 @@ __forceinline static int php_win32_ioutil_stat_ex(const char *path, php_win32_io
#define php_win32_ioutil_stat(path, buf) php_win32_ioutil_stat_ex(path, buf, 0)
#define php_win32_ioutil_lstat(path, buf) php_win32_ioutil_stat_ex(path, buf, 1)
+PW32IO ssize_t php_win32_ioutil_readlink_w(const wchar_t *path, wchar_t *buf, size_t buf_len);
+
+__forceinline static ssize_t php_win32_ioutil_readlink(const char *path, char *buf, size_t buf_len)
+{/*{{{*/
+ size_t pathw_len, ret_buf_len;
+ wchar_t *pathw = php_win32_ioutil_conv_any_to_w(path, PHP_WIN32_CP_IGNORE_LEN, &pathw_len);
+ wchar_t retw[PHP_WIN32_IOUTIL_MAXPATHLEN];
+ char *ret_buf;
+ ssize_t ret;
+
+ if (!pathw) {
+ SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
+ return -1;
+ }
+
+ ret = php_win32_ioutil_readlink_w(pathw, retw, sizeof(retw)-1);
+ if (ret < 0) {
+ DWORD _err = GetLastError();
+ free(pathw);
+ SET_ERRNO_FROM_WIN32_CODE(_err);
+ return ret;
+ }
+
+ ret_buf = php_win32_ioutil_conv_w_to_any(retw, PHP_WIN32_CP_IGNORE_LEN, &ret_buf_len);
+ if (!ret_buf || ret_buf_len >= buf_len || ret_buf_len >= MAXPATHLEN) {
+ free(pathw);
+ SET_ERRNO_FROM_WIN32_CODE(ERROR_BAD_PATHNAME);
+ return -1;
+ }
+ memcpy(buf, ret_buf, ret_buf_len + 1);
+
+ free(pathw);
+
+ return ret;
+}/*}}}*/
+
#ifdef __cplusplus
}
#endif