1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
base / files / file_enumerator_win.cc [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/file_enumerator.h"
#include <stdint.h>
#include <string.h>
#include "base/check.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/shlwapi.h"
namespace base {
namespace {
FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
const FilePath& root_path,
const FilePath::StringType& pattern) {
// MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy
// collects all files and filters them manually.
switch (policy) {
case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
return root_path.Append(pattern);
case FileEnumerator::FolderSearchPolicy::ALL:
return root_path.Append(FILE_PATH_LITERAL("*"));
}
NOTREACHED();
}
} // namespace
// FileEnumerator::FileInfo ----------------------------------------------------
FileEnumerator::FileInfo::FileInfo() {
memset(&find_data_, 0, sizeof(find_data_));
}
bool FileEnumerator::FileInfo::IsDirectory() const {
return (find_data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
FilePath FileEnumerator::FileInfo::GetName() const {
return FilePath(find_data().cFileName);
}
int64_t FileEnumerator::FileInfo::GetSize() const {
ULARGE_INTEGER size;
size.HighPart = find_data().nFileSizeHigh;
size.LowPart = find_data().nFileSizeLow;
DCHECK_LE(size.QuadPart,
static_cast<ULONGLONG>(std::numeric_limits<int64_t>::max()));
return static_cast<int64_t>(size.QuadPart);
}
Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
return Time::FromFileTime(find_data().ftLastWriteTime);
}
// FileEnumerator --------------------------------------------------------------
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type)
: FileEnumerator(root_path,
recursive,
file_type,
FilePath::StringType(),
FolderSearchPolicy::MATCH_ONLY) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern)
: FileEnumerator(root_path,
recursive,
file_type,
pattern,
FolderSearchPolicy::MATCH_ONLY) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern,
FolderSearchPolicy folder_search_policy)
: FileEnumerator(root_path,
recursive,
file_type,
pattern,
folder_search_policy,
ErrorPolicy::IGNORE_ERRORS) {}
FileEnumerator::FileEnumerator(const FilePath& root_path,
bool recursive,
int file_type,
const FilePath::StringType& pattern,
FolderSearchPolicy folder_search_policy,
ErrorPolicy error_policy)
: recursive_(recursive),
file_type_(file_type),
pattern_(!pattern.empty() ? pattern : FILE_PATH_LITERAL("*")),
folder_search_policy_(folder_search_policy),
error_policy_(error_policy) {
// INCLUDE_DOT_DOT must not be specified if recursive.
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
if (file_type_ & FileType::NAMES_ONLY) {
DCHECK(!recursive_);
DCHECK_EQ(file_type_ & ~(FileType::NAMES_ONLY | FileType::INCLUDE_DOT_DOT),
0);
file_type_ |= (FileType::FILES | FileType::DIRECTORIES);
}
memset(&find_data_, 0, sizeof(find_data_));
pending_paths_.push(root_path);
}
FileEnumerator::~FileEnumerator() {
if (find_handle_ != INVALID_HANDLE_VALUE)
FindClose(find_handle_);
}
FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
DCHECK(!(file_type_ & FileType::NAMES_ONLY));
CHECK(has_find_data_);
FileInfo ret;
memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
return ret;
}
FilePath FileEnumerator::Next() {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
while (has_find_data_ || !pending_paths_.empty()) {
if (!has_find_data_) {
// The last find FindFirstFile operation is done, prepare a new one.
root_path_ = pending_paths_.top();
pending_paths_.pop();
// Start a new find operation.
const FilePath src =
BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
find_handle_ = FindFirstFileEx(src.value().c_str(),
FindExInfoBasic, // Omit short name.
ChromeToWindowsType(&find_data_),
FindExSearchNameMatch, nullptr,
FIND_FIRST_EX_LARGE_FETCH);
has_find_data_ = true;
} else {
// Search for the next file/directory.
if (!FindNextFile(find_handle_, ChromeToWindowsType(&find_data_))) {
FindClose(find_handle_);
find_handle_ = INVALID_HANDLE_VALUE;
}
}
DWORD last_error = GetLastError();
if (INVALID_HANDLE_VALUE == find_handle_) {
has_find_data_ = false;
// MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
// applies pattern for all subfolders.
if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
// This is reached when we have finished a directory and are advancing
// to the next one in the queue. We applied the pattern (if any) to the
// files in the root search directory, but for those directories which
// were matched, we want to enumerate all files inside them. This will
// happen when the handle is empty.
pattern_ = FILE_PATH_LITERAL("*");
}
if (last_error == ERROR_NO_MORE_FILES ||
error_policy_ == ErrorPolicy::IGNORE_ERRORS) {
continue;
}
error_ = File::OSErrorToFileError(last_error);
return FilePath();
}
const FilePath filename(find_data().cFileName);
if (ShouldSkip(filename))
continue;
const bool is_dir =
(find_data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
const FilePath abs_path = root_path_.Append(filename);
// Check if directory should be processed recursive.
if (is_dir && recursive_) {
// If |cur_file| is a directory, and we are doing recursive searching,
// add it to pending_paths_ so we scan it after we finish scanning this
// directory. However, don't do recursion through reparse points or we
// may end up with an infinite cycle.
DWORD attributes = GetFileAttributes(abs_path.value().c_str());
if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
pending_paths_.push(abs_path);
}
if (IsTypeMatched(is_dir) && IsPatternMatched(filename))
return abs_path;
}
return FilePath();
}
bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
switch (folder_search_policy_) {
case FolderSearchPolicy::MATCH_ONLY:
// MATCH_ONLY policy filters by pattern on search request, so all found
// files already fits to pattern.
return true;
case FolderSearchPolicy::ALL:
// ALL policy enumerates all files, we need to check pattern match
// manually.
return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE;
}
NOTREACHED();
}
} // namespace base