Skip to content

Commit 99fe185

Browse files
committed
Fixed RecursiveDirectoryIterator with long path or with edge case length
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.
1 parent 236ae06 commit 99fe185

File tree

2 files changed

+103
-9
lines changed

2 files changed

+103
-9
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
--TEST--
2+
RecursiveDirectoryIterator with dir path long or of edge case length
3+
--SKIPIF--
4+
<?php
5+
include dirname(__FILE__) . DIRECTORY_SEPARATOR . "util.inc";
6+
7+
skip_if_not_win();
8+
9+
if (strlen(dirname(__FILE__)) > 259) die("Unsuitable starting path length");
10+
?>
11+
--FILE--
12+
<?php
13+
14+
$need_len = 1024;
15+
//$need_len = 259;
16+
$dir = dirname(__FILE__);
17+
while ($need_len - strlen($dir) > 32) {
18+
$dir .= DIRECTORY_SEPARATOR . str_repeat("a", 32);
19+
}
20+
$dir .= DIRECTORY_SEPARATOR . str_repeat("a", $need_len - strlen($dir));
21+
mkdir($dir, 0700, true);
22+
23+
$fl = $dir . DIRECTORY_SEPARATOR . "hello.txt";
24+
file_put_contents($fl, "");
25+
26+
27+
$start = substr($dir, 0, strpos($dir, DIRECTORY_SEPARATOR, strlen(dirname(__FILE__))+1));
28+
$iter = new RecursiveIteratorIterator(
29+
new RecursiveDirectoryIterator(
30+
$start,
31+
FilesystemIterator::SKIP_DOTS
32+
),
33+
RecursiveIteratorIterator::CHILD_FIRST
34+
);
35+
36+
foreach ($iter as $item) {
37+
if (!$item->isDir()) {
38+
var_dump($item->getPathname());
39+
}
40+
}
41+
42+
$iter->rewind();
43+
foreach ($iter as $item) {
44+
if ($item->isDir()) {
45+
rmdir($item->getPathname());
46+
} else {
47+
unlink($item->getPathname());
48+
}
49+
}
50+
rmdir($start);
51+
var_dump(file_exists($start));
52+
53+
/*unlink($fl);
54+
do {
55+
rmdir($dir);
56+
$dir = dirname($dir);
57+
} while (dirname(__FILE__) != $dir);*/
58+
59+
?>
60+
==DONE==
61+
--EXPECTF--
62+
string(%d) "%shello.txt"
63+
bool(false)
64+
==DONE==
65+

win32/readdir.c

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ DIR *opendir(const char *dir)
3939
DIR *dp;
4040
wchar_t *filespecw, *resolvedw;
4141
HANDLE handle;
42-
int index;
4342
char resolved_path_buff[MAXPATHLEN];
44-
size_t resolvedw_len, filespecw_len;
43+
size_t resolvedw_len, filespecw_len, index;
44+
zend_bool might_need_prefix;
4545

4646
if (!VCWD_REALPATH(dir, resolved_path_buff)) {
4747
return NULL;
@@ -58,16 +58,27 @@ DIR *opendir(const char *dir)
5858
return NULL;
5959
}
6060

61+
might_need_prefix = resolvedw_len >= 3 && PHP_WIN32_IOUTIL_IS_LETTERW(resolvedw[0]) && L':' == resolvedw[1] && PHP_WIN32_IOUTIL_IS_SLASHW(resolvedw[2]);
62+
6163
filespecw_len = resolvedw_len + 2;
64+
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
65+
filespecw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
66+
}
6267
filespecw = (wchar_t *)malloc((filespecw_len + 1)*sizeof(wchar_t));
6368
if (filespecw == NULL) {
6469
free(dp);
6570
free(resolvedw);
6671
return NULL;
6772
}
6873

69-
wcscpy(filespecw, resolvedw);
70-
index = (int)filespecw_len - 1;
74+
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
75+
wcscpy(filespecw, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW);
76+
wcscpy(filespecw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, resolvedw);
77+
index = resolvedw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW - 1;
78+
} else {
79+
wcscpy(filespecw, resolvedw);
80+
index = resolvedw_len - 1;
81+
}
7182
if (index >= 0 && filespecw[index] == L'/' || index == 0 && filespecw[index] == L'\\')
7283
filespecw[index] = L'\0';
7384
wcscat(filespecw, L"\\*");
@@ -186,24 +197,42 @@ int rewinddir(DIR *dp)
186197
/* Re-set to the beginning */
187198
wchar_t *filespecw;
188199
HANDLE handle;
189-
int index;
200+
size_t dirw_len, filespecw_len, index;
201+
zend_bool might_need_prefix;
190202

191203
FindClose(dp->handle);
192204

193205
dp->offset = 0;
194206
dp->finished = 0;
195207

196-
filespecw = (wchar_t *)malloc((wcslen((wchar_t *)dp->dirw) + 2 + 1)*sizeof(wchar_t));
208+
/* XXX save the dir len into the struct. */
209+
dirw_len = wcslen((wchar_t *)dp->dirw);
210+
211+
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]);
212+
213+
filespecw_len = dirw_len + 2;
214+
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
215+
filespecw_len += PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW;
216+
}
217+
218+
filespecw = (wchar_t *)malloc((filespecw_len + 1)*sizeof(wchar_t));
197219
if (filespecw == NULL) {
198220
return -1;
199221
}
200222

201-
wcscpy(filespecw, (wchar_t *)dp->dirw);
202-
index = (int)wcslen(filespecw) - 1;
223+
if (filespecw_len >= _MAX_PATH && might_need_prefix) {
224+
wcscpy(filespecw, PHP_WIN32_IOUTIL_LONG_PATH_PREFIXW);
225+
wcscpy(filespecw + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW, dp->dirw);
226+
index = dirw_len + PHP_WIN32_IOUTIL_LONG_PATH_PREFIX_LENW - 1;
227+
} else {
228+
wcscpy(filespecw, dp->dirw);
229+
index = dirw_len - 1;
230+
}
231+
203232
if (index >= 0 && (filespecw[index] == L'/' ||
204233
(filespecw[index] == L'\\' && index == 0)))
205234
filespecw[index] = L'\0';
206-
wcscat(filespecw, L"/*");
235+
wcscat(filespecw, L"\\*");
207236

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

0 commit comments

Comments
 (0)