]> granicus.if.org Git - php/commitdiff
Refactor php_sys_readlink
authorAnatol Belski <ab@php.net>
Wed, 3 Oct 2018 16:56:08 +0000 (18:56 +0200)
committerAnatol Belski <ab@php.net>
Wed, 3 Oct 2018 16:56:55 +0000 (18:56 +0200)
Also move the implementation into win32 where it belongs

Zend/zend_virtual_cwd.c
Zend/zend_virtual_cwd.h
win32/ftok.c
win32/ioutil.c
win32/ioutil.h

index 079fab3eb6db524049e50ad9114dbd95bde54c94..16734501ecb96249658a3216ac0eae90f5c9365c 100644 (file)
@@ -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;
index 9773ef1e48b361060a6e18375958e447bcb9fd5e..afee6ee7d449075ad48b2435370c3e2646192492 100644 (file)
@@ -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
index 469f2df59af324064759875440059726ab717e4e..77de6a0449c15220bea90e4da83d6d3864eb8901 100644 (file)
@@ -16,6 +16,7 @@
    +----------------------------------------------------------------------+
  */
 
+#include "php.h"
 #include "ipc.h"
 
 #include <windows.h>
index 99eccd511fb99edee0aaa2a95f9d2f016289a1ec..0ca01261a52cb6d2b88260c0a7fe85b8b039b51a 100644 (file)
@@ -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
index 9da518dbc42654d0f3f96242ccdb38bc95ea4ee6..db21c449e0f16b381cf7856c88db8e8dbbf66def 100644 (file)
@@ -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