]> granicus.if.org Git - php/commitdiff
Fixed bug #73877 readlink() returns garbage for UTF-8 paths
authorAnatol Belski <ab@php.net>
Sat, 7 Jan 2017 00:09:17 +0000 (01:09 +0100)
committerAnatol Belski <ab@php.net>
Sat, 7 Jan 2017 00:09:17 +0000 (01:09 +0100)
NEWS
Zend/zend_virtual_cwd.c
ext/standard/tests/dir/bug73877.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index d563dcc17b9e6277ab2ce662a74254087231fbe2..8a385eebf35d8e2bb0db758e6813a89148de78f8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@ PHP                                                                        NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? 2017, PHP 7.1.2
 
+- Core:
+  . Fixed bug #73877 (readlink() returns garbage for UTF-8 paths). (Anatol)
+
 - OpenSSL:
   . Fixed bug #71519 (add serial hex to return value array). (xrobau)
 
index c159bb93d98690d5df1ff8dfc37ae4da3dc70d9d..5ab3d5431536986c6fa6553b27ec0777cf4b1073 100644 (file)
@@ -219,10 +219,9 @@ static inline time_t FileTimeToUnixTime(const FILETIME *FileTime)
 
 CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){ /* {{{ */
        HANDLE hFile;
-       DWORD dwRet;
        wchar_t *linkw = php_win32_ioutil_any_to_w(link), targetw[MAXPATHLEN];
-       size_t _tmp_len;
-       char *_tmp;
+       size_t ret_len, targetw_len, offset = 0;
+       char *ret;
 
        if (!linkw) {
                return -1;
@@ -251,46 +250,39 @@ CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len){
                with VS2012 and earlier, and seems not to be fixed till
                now. Thus, correcting target_len so it's suddenly don't
                overflown. */
-       dwRet = GetFinalPathNameByHandleW(hFile, targetw, MAXPATHLEN, VOLUME_NAME_DOS);
-       if(dwRet >= target_len || dwRet >= MAXPATHLEN || dwRet == 0) {
+       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;
        }
 
-       _tmp = php_win32_ioutil_conv_w_to_any(targetw, dwRet, &_tmp_len);
-       if (!_tmp || _tmp_len >= MAXPATHLEN) {
+       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);
                return -1;
        }
-       memcpy(target, _tmp, _tmp_len);
-       free(_tmp);
+       memcpy(target, ret, ret_len + 1);
+       free(ret);
 
        CloseHandle(hFile);
        free(linkw);
 
-       if(dwRet > 4) {
-               /* Skip first 4 characters if they are "\??\" */
-               if(target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] ==  '\\') {
-                       char tmp[MAXPATHLEN];
-                       unsigned int offset = 4;
-                       dwRet -= 4;
-
-                       /* \??\UNC\ */
-                       if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
-                               offset += 2;
-                               dwRet -= 2;
-                               target[offset] = '\\';
-                       }
-
-                       memcpy(tmp, target + offset, dwRet);
-                       memcpy(target, tmp, dwRet);
-               }
-       }
-
-       target[dwRet] = '\0';
-       return dwRet;
+       return ret_len;
 }
 /* }}} */
 
diff --git a/ext/standard/tests/dir/bug73877.phpt b/ext/standard/tests/dir/bug73877.phpt
new file mode 100644 (file)
index 0000000..bade168
--- /dev/null
@@ -0,0 +1,50 @@
+--TEST--
+Bug #73877 readlink() returns garbage for UTF-8 paths
+File type functions
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+    die('skip only for Windows');
+}
+?>
+--FILE--
+<?php
+
+$base = dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug73877";
+$dir0 = $base . DIRECTORY_SEPARATOR . "bug73877";
+$dir1 = $base . DIRECTORY_SEPARATOR . "Серёжка";
+$junk0 = $base . DIRECTORY_SEPARATOR . "Серёжка2";
+
+mkdir($base);
+mkdir($dir0);
+mkdir($dir1);
+`mklink /J $junk0 $dir0`;
+
+var_dump(
+       readlink($dir0),
+       readlink($dir1),
+       readlink($junk0),
+       strlen(readlink($dir0)) === strlen(readlink($junk0))
+);
+
+?>
+--CLEAN--
+<?php
+
+$base = dirname(__FILE__) . DIRECTORY_SEPARATOR . "bug73877";
+$dir0 = $base . DIRECTORY_SEPARATOR . "bug73877";
+$dir1 = $base . DIRECTORY_SEPARATOR . "Серёжка";
+$junk0 = $base . DIRECTORY_SEPARATOR . "Серёжка2";
+
+rmdir($junk0);
+rmdir($dir0);
+rmdir($dir1);
+rmdir($base);
+
+?>
+--EXPECTF--
+string(%d) "%sbug73877"
+string(%d) "%sСерёжка"
+string(%d) "%sbug73877"
+bool(true)
+