summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph M. Becker <cmbecker69@gmx.de>2019-08-19 19:44:37 +0200
committerChristoph M. Becker <cmbecker69@gmx.de>2019-08-19 19:55:35 +0200
commit81f52158b42318f17f67468cccc4a8dc03bba942 (patch)
tree13bb858a348ac752b65ff25fa875dc98a4b1ff31
parent725f439778a6397267fbb4e88059d704cccbc7f3 (diff)
downloadphp-git-81f52158b42318f17f67468cccc4a8dc03bba942.tar.gz
Fix #78220: Can't access OneDrive folder
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.
-rw-r--r--NEWS1
-rw-r--r--Zend/zend_virtual_cwd.c20
-rw-r--r--ext/standard/tests/dir/bug78220.phpt16
3 files changed, 35 insertions, 2 deletions
diff --git a/NEWS b/NEWS
index 8ad64104fb..bcec73d1a7 100644
--- 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)
diff --git a/Zend/zend_virtual_cwd.c b/Zend/zend_virtual_cwd.c
index f9395f32a4..563e121b7e 100644
--- a/Zend/zend_virtual_cwd.c
+++ b/Zend/zend_virtual_cwd.c
@@ -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
index 0000000000..16daa0d716
--- /dev/null
+++ b/ext/standard/tests/dir/bug78220.phpt
@@ -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--