]> granicus.if.org Git - php/commitdiff
Fix #78220: Can't access OneDrive folder
authorChristoph M. Becker <cmbecker69@gmx.de>
Mon, 19 Aug 2019 17:44:37 +0000 (19:44 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Mon, 19 Aug 2019 17:55:35 +0000 (19:55 +0200)
As of Windows 1903, when the OneDrive on-demand feature is enabled, the
OneDrive folder is reported as reparse point by `FindFirstFile()`, but
trying to get information about the reparse point using
`DeviceIoControl()` fails with `ERROR_NOT_A_REPARSE_POINT`.  We work
around this problem by falling back to `GetFileInformationByHandle()`
if that happens, but only if the reparse point is reported as cloud
reparse point, and only if PHP is running on Windows 1903 or later.

The patch has been developed in collaboration with ab@php.net.

We should keep an eye on the somewhat quirky OneDrive behavior, since
it might change again in a future Windows release.

NEWS
Zend/zend_virtual_cwd.c
ext/standard/tests/dir/bug78220.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 8ad64104fb37d81ca7335fdac77e0d0f8552b9fd..bcec73d1a7454ef7f648a5c4afa5f5a0fc8e94bc 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ PHP                                                                        NEWS
 ?? ??? 2019, PHP 7.2.23
 
 - Core:
+  . Fixed bug #78220 (Can't access OneDrive folder). (cmb, ab)
   . Fixed bug #78412 (Generator incorrectly reports non-releasable $this as GC
     child). (Nikita)
 
index f9395f32a43688b8bec60ea1ae5fc752c7eac1cf..563e121b7e3da4e1f2b1a8735110b4016d4babee 100644 (file)
@@ -91,6 +91,8 @@ cwd_state main_cwd_state; /* True global */
 #include <unistd.h>
 #else
 #include <direct.h>
+#include "zend_globals.h"
+#include "zend_globals_macros.h"
 #endif
 
 #define CWD_STATE_COPY(d, s)                           \
@@ -855,6 +857,7 @@ static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, i
                tmp = do_alloca(len+1, use_heap);
                memcpy(tmp, path, len+1);
 
+retry:
                if(save &&
                                !(IS_UNC_PATH(path, len) && len >= 3 && path[2] != '?') &&
                                (dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
@@ -895,7 +898,21 @@ static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, i
                                return -1;
                        }
                        if(!DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, pbuffer,  MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &retlength, NULL)) {
+                               BY_HANDLE_FILE_INFORMATION fileInformation;
+
                                free_alloca(pbuffer, use_heap_large);
+                               if ((dataw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+                                               (dataw.dwReserved0 & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD &&
+                                               EG(windows_version_info).dwMajorVersion >= 10 &&
+                                               EG(windows_version_info).dwMinorVersion == 0 &&
+                                               EG(windows_version_info).dwBuildNumber >= 18362 &&
+                                               GetFileInformationByHandle(hLink, &fileInformation) &&
+                                               !(fileInformation.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+                                       dataw.dwFileAttributes = fileInformation.dwFileAttributes;
+                                       CloseHandle(hLink);
+                                       (*ll)--;
+                                       goto retry;
+                               }
                                free_alloca(tmp, use_heap);
                                CloseHandle(hLink);
                                FREE_PATHW()
@@ -975,8 +992,7 @@ static int tsrm_realpath_r(char *path, int start, int len, int *ll, time_t *t, i
                        }
                        else if (pbuffer->ReparseTag == IO_REPARSE_TAG_DEDUP ||
                                        /* Starting with 1709. */
-                                       (pbuffer->ReparseTag & IO_REPARSE_TAG_CLOUD_MASK) != 0 && 0x90001018L != pbuffer->ReparseTag ||
-                                       IO_REPARSE_TAG_CLOUD == pbuffer->ReparseTag ||
+                                       (pbuffer->ReparseTag & ~IO_REPARSE_TAG_CLOUD_MASK) == IO_REPARSE_TAG_CLOUD ||
                                        IO_REPARSE_TAG_ONEDRIVE == pbuffer->ReparseTag) {
                                isabsolute = 1;
                                substitutename = malloc((len + 1) * sizeof(char));
diff --git a/ext/standard/tests/dir/bug78220.phpt b/ext/standard/tests/dir/bug78220.phpt
new file mode 100644 (file)
index 0000000..16daa0d
--- /dev/null
@@ -0,0 +1,16 @@
+--TEST--
+Bug #78220 (Can't access OneDrive folder)
+--SKIPIF--
+<?php
+if (substr(PHP_OS, 0, 3) != 'WIN') die("skip this test is for Windows platforms only");
+?>
+--FILE--
+<?php
+$onedrive_dirs = array_unique([getenv('OneDrive'), getenv('OneDriveCommercial')]);
+foreach ($onedrive_dirs as $dir) {
+    if ($dir && scandir($dir) === FALSE) {
+        echo "can't scan $dir\n";
+    }
+}
+?>
+--EXPECT--