Skip to content

Commit

Permalink
Fixed RecursiveDirectoryIterator with long path or with edge case length
Browse files Browse the repository at this point in the history
The search path needs to be appended with the wild card. Till now, an
edge case existed, so then if a path is 259 bytes long, which is smaller
_MAX_PATH, the suffix would cause the final search path to become longer
than _MAX_PATH. It is an edge case, when the starting path happens to
have a specific length. If the starting path was longer than _MAX_PATH
or the addition of "\\*" would not exceed _MAX_PATH, the function was
correct. Except for rewind, which was broken in the case of the long
path.
  • Loading branch information
weltling committed Jul 12, 2018
1 parent 236ae06 commit 99fe185
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 9 deletions.
65 changes: 65 additions & 0 deletions ext/standard/tests/file/windows_mb_path/recursive_it.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
--TEST--
RecursiveDirectoryIterator with dir path long or of edge case length
--SKIPIF--
<?php
include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";

skip_if_not_win();

if (strlen(dirname(__FILE__)) > 259) die("Unsuitable starting path length");
?>
--FILE--
<?php

$need_len = 1024;
//$need_len = 259;
$dir = dirname(__FILE__);
while ($need_len - strlen($dir) > 32) {
$dir .= DIRECTORY_SEPARATOR . str_repeat("a", 32);
}
$dir .= DIRECTORY_SEPARATOR . str_repeat("a", $need_len - strlen($dir));
mkdir($dir, 0700, true);

$fl = $dir . DIRECTORY_SEPARATOR . "hello.txt";
file_put_contents($fl, "");


$start = substr($dir, 0, strpos($dir, DIRECTORY_SEPARATOR, strlen(dirname(__FILE__))+1));
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$start,
FilesystemIterator::SKIP_DOTS
),
RecursiveIteratorIterator::CHILD_FIRST
);

foreach ($iter as $item) {
if (!$item->isDir()) {
var_dump($item->getPathname());
}
}

$iter->rewind();
foreach ($iter as $item) {
if ($item->isDir()) {
rmdir($item->getPathname());
} else {
unlink($item->getPathname());
}
}
rmdir($start);
var_dump(file_exists($start));

/*unlink($fl);
do {
rmdir($dir);
$dir = dirname($dir);
} while (dirname(__FILE__) != $dir);*/

?>
==DONE==
--EXPECTF--
string(%d) "%shello.txt"
bool(false)
==DONE==

47 changes: 38 additions & 9 deletions win32/readdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ DIR *opendir(const char *dir)
DIR *dp;
wchar_t *filespecw, *resolvedw;
HANDLE handle;
int index;
char resolved_path_buff[MAXPATHLEN];
size_t resolvedw_len, filespecw_len;
size_t resolvedw_len, filespecw_len, index;
zend_bool might_need_prefix;

if (!VCWD_REALPATH(dir, resolved_path_buff)) {
return NULL;
Expand All @@ -58,16 +58,27 @@ DIR *opendir(const char *dir)
return NULL;
}

might_need_prefix = resolvedw_len >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(resolvedw[0]) && L':' == resolvedw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(resolvedw[2]);

filespecw_len = resolvedw_len + 2;
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
filespecw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
}
filespecw = (wchar_t *)malloc((filespecw_len + 1)*sizeof(wchar_t));
if (filespecw == NULL) {
free(dp);
free(resolvedw);
return NULL;
}

wcscpy(filespecw, resolvedw);
index = (int)filespecw_len - 1;
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
wcscpy(filespecw, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW);
wcscpy(filespecw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, resolvedw);
index = resolvedw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW - 1;
} else {
wcscpy(filespecw, resolvedw);
index = resolvedw_len - 1;
}
if (index >= 0 && filespecw[index] == L'/' || index == 0 && filespecw[index] == L'\\')
filespecw[index] = L'\0';
wcscat(filespecw, L"\\*");
Expand Down Expand Up @@ -186,24 +197,42 @@ int rewinddir(DIR *dp)
/* Re-set to the beginning */
wchar_t *filespecw;
HANDLE handle;
int index;
size_t dirw_len, filespecw_len, index;
zend_bool might_need_prefix;

FindClose(dp->handle);

dp->offset = 0;
dp->finished = 0;

filespecw = (wchar_t *)malloc((wcslen((wchar_t *)dp->dirw) + 2 + 1)*sizeof(wchar_t));
/* XXX save the dir len into the struct. */
dirw_len = wcslen((wchar_t *)dp->dirw);

might_need_prefix = dirw_len >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(dp->dirw[0]) && L':' == dp->dirw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(dp->dirw[2]);

filespecw_len = dirw_len + 2;
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
filespecw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
}

filespecw = (wchar_t *)malloc((filespecw_len + 1)*sizeof(wchar_t));
if (filespecw == NULL) {
return -1;
}

wcscpy(filespecw, (wchar_t *)dp->dirw);
index = (int)wcslen(filespecw) - 1;
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
wcscpy(filespecw, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW);
wcscpy(filespecw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, dp->dirw);
index = dirw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW - 1;
} else {
wcscpy(filespecw, dp->dirw);
index = dirw_len - 1;
}

if (index >= 0 && (filespecw[index] == L'/' ||
(filespecw[index] == L'\\' && index == 0)))
filespecw[index] = L'\0';
wcscat(filespecw, L"/*");
wcscat(filespecw, L"\\*");

if ((handle = FindFirstFileW(filespecw, &(dp->fileinfo))) == INVALID_HANDLE_VALUE) {
dp->finished = 1;
Expand Down

0 comments on commit 99fe185

Please sign in to comment.