Browse Source

Refactor php_sys_readlink

Also move the implementation into win32 where it belongs
pull/3574/head
Anatol Belski 7 years ago
parent
commit
91c905e83c
  1. 72
      Zend/zend_virtual_cwd.c
  2. 2
      Zend/zend_virtual_cwd.h
  3. 1
      win32/ftok.c
  4. 132
      win32/ioutil.c
  5. 36
      win32/ioutil.h

72
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;

2
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

1
win32/ftok.c

@ -16,6 +16,7 @@
+----------------------------------------------------------------------+
*/
#include "php.h"
#include "ipc.h"
#include <windows.h>

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

36
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

Loading…
Cancel
Save