diff options
author | Anatol Belski <ab@php.net> | 2018-10-03 18:56:08 +0200 |
---|---|---|
committer | Anatol Belski <ab@php.net> | 2018-10-03 18:56:55 +0200 |
commit | 91c905e83c338ef66da824be4f90c1d78d134507 (patch) | |
tree | 6095583ab22193f27d4837f53d1f4f5e5a5850d8 /win32 | |
parent | 97481fccb9265df7e813f8c4e3f25d3a20651ce2 (diff) | |
download | php-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.c | 1 | ||||
-rw-r--r-- | win32/ioutil.c | 132 | ||||
-rw-r--r-- | win32/ioutil.h | 36 |
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 |