summaryrefslogtreecommitdiff
path: root/win32
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 /win32
parent97481fccb9265df7e813f8c4e3f25d3a20651ce2 (diff)
downloadphp-git-91c905e83c338ef66da824be4f90c1d78d134507.tar.gz
Refactor php_sys_readlink
Also move the implementation into win32 where it belongs
Diffstat (limited to 'win32')
-rw-r--r--win32/ftok.c1
-rw-r--r--win32/ioutil.c132
-rw-r--r--win32/ioutil.h36
3 files changed, 169 insertions, 0 deletions
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