mirror of
https://github.com/php/php-src.git
synced 2024-11-23 09:54:15 +08:00
Refactor php_sys_readlink
Also move the implementation into win32 where it belongs
This commit is contained in:
parent
97481fccb9
commit
91c905e83c
@ -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;
|
||||
|
@ -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
|
||||
|
@ -16,6 +16,7 @@
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "php.h"
|
||||
#include "ipc.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
132
win32/ioutil.c
132
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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user