Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(411)

Side by Side Diff: base/files/file_enumerator_win.cc

Issue 2892173003: Add recursive pattern matching for subfolders in file_enumerator. (Closed)
Patch Set: Add recursive pattern matching for subfolders in file_enumerator Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_enumerator.h" 5 #include "base/files/file_enumerator.h"
6 6
7 #include <shlwapi.h>
7 #include <stdint.h> 8 #include <stdint.h>
8 #include <string.h> 9 #include <string.h>
9 10
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
12 #include "base/win/windows_version.h" 13 #include "base/win/windows_version.h"
13 14
14 namespace base { 15 namespace base {
15 16
17 namespace {
18
19 FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,
20 const FilePath& root_path,
21 const FilePath::StringType& pattern) {
22 // MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy
23 // collects all files and filters them manually.
24 switch (policy) {
25 case FileEnumerator::FolderSearchPolicy::MATCH_ONLY:
26 return root_path.Append(pattern);
27 case FileEnumerator::FolderSearchPolicy::ALL:
28 return root_path.AppendASCII("*");
Lei Zhang 2017/06/08 08:21:19 Can you do Append(L"*") here to avoid an ASCIIToUT
ivafanas 2017/06/16 11:13:16 Done.
29 }
30 NOTREACHED();
31 return {};
32 }
33
34 } // namespace
35
16 // FileEnumerator::FileInfo ---------------------------------------------------- 36 // FileEnumerator::FileInfo ----------------------------------------------------
17 37
18 FileEnumerator::FileInfo::FileInfo() { 38 FileEnumerator::FileInfo::FileInfo() {
19 memset(&find_data_, 0, sizeof(find_data_)); 39 memset(&find_data_, 0, sizeof(find_data_));
20 } 40 }
21 41
22 bool FileEnumerator::FileInfo::IsDirectory() const { 42 bool FileEnumerator::FileInfo::IsDirectory() const {
23 return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; 43 return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
24 } 44 }
25 45
(...skipping 12 matching lines...) Expand all
38 58
39 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { 59 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
40 return base::Time::FromFileTime(find_data_.ftLastWriteTime); 60 return base::Time::FromFileTime(find_data_.ftLastWriteTime);
41 } 61 }
42 62
43 // FileEnumerator -------------------------------------------------------------- 63 // FileEnumerator --------------------------------------------------------------
44 64
45 FileEnumerator::FileEnumerator(const FilePath& root_path, 65 FileEnumerator::FileEnumerator(const FilePath& root_path,
46 bool recursive, 66 bool recursive,
47 int file_type) 67 int file_type)
48 : has_find_data_(false), 68 : FileEnumerator(root_path,
49 find_handle_(INVALID_HANDLE_VALUE), 69 recursive,
50 recursive_(recursive), 70 file_type,
51 file_type_(file_type) { 71 FilePath::StringType(),
52 // INCLUDE_DOT_DOT must not be specified if recursive. 72 FolderSearchPolicy::MATCH_ONLY) {}
53 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
54 memset(&find_data_, 0, sizeof(find_data_));
55 pending_paths_.push(root_path);
56 }
57 73
58 FileEnumerator::FileEnumerator(const FilePath& root_path, 74 FileEnumerator::FileEnumerator(const FilePath& root_path,
59 bool recursive, 75 bool recursive,
60 int file_type, 76 int file_type,
61 const FilePath::StringType& pattern) 77 const FilePath::StringType& pattern)
62 : has_find_data_(false), 78 : FileEnumerator(root_path,
63 find_handle_(INVALID_HANDLE_VALUE), 79 recursive,
64 recursive_(recursive), 80 file_type,
81 pattern,
82 FolderSearchPolicy::MATCH_ONLY) {}
83
84 FileEnumerator::FileEnumerator(const FilePath& root_path,
85 bool recursive,
86 int file_type,
87 const FilePath::StringType& pattern,
88 FolderSearchPolicy folder_search_policy)
89 : recursive_(recursive),
65 file_type_(file_type), 90 file_type_(file_type),
66 pattern_(pattern) { 91 pattern_(!pattern.empty() ? pattern : FILE_PATH_LITERAL("*")),
92 folder_search_policy_(folder_search_policy) {
67 // INCLUDE_DOT_DOT must not be specified if recursive. 93 // INCLUDE_DOT_DOT must not be specified if recursive.
68 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); 94 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
69 memset(&find_data_, 0, sizeof(find_data_)); 95 memset(&find_data_, 0, sizeof(find_data_));
70 pending_paths_.push(root_path); 96 pending_paths_.push(root_path);
71 } 97 }
72 98
73 FileEnumerator::~FileEnumerator() { 99 FileEnumerator::~FileEnumerator() {
74 if (find_handle_ != INVALID_HANDLE_VALUE) 100 if (find_handle_ != INVALID_HANDLE_VALUE)
75 FindClose(find_handle_); 101 FindClose(find_handle_);
76 } 102 }
(...skipping 11 matching lines...) Expand all
88 FilePath FileEnumerator::Next() { 114 FilePath FileEnumerator::Next() {
89 base::ThreadRestrictions::AssertIOAllowed(); 115 base::ThreadRestrictions::AssertIOAllowed();
90 116
91 while (has_find_data_ || !pending_paths_.empty()) { 117 while (has_find_data_ || !pending_paths_.empty()) {
92 if (!has_find_data_) { 118 if (!has_find_data_) {
93 // The last find FindFirstFile operation is done, prepare a new one. 119 // The last find FindFirstFile operation is done, prepare a new one.
94 root_path_ = pending_paths_.top(); 120 root_path_ = pending_paths_.top();
95 pending_paths_.pop(); 121 pending_paths_.pop();
96 122
97 // Start a new find operation. 123 // Start a new find operation.
98 FilePath src = root_path_; 124 const FilePath src =
99 125 BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
100 if (pattern_.empty()) 126 find_handle_ = FindFirstFileEx(src.value().c_str(),
101 src = src.Append(L"*"); // No pattern = match everything. 127 FindExInfoBasic, // Omit short name.
Lei Zhang 2017/06/08 08:21:19 Is this special case no longer needed in BuildSear
ivafanas 2017/06/16 11:13:15 The pattern is never empty. Yes, there is always-f
102 else 128 &find_data_, FindExSearchNameMatch,
103 src = src.Append(pattern_); 129 nullptr, FIND_FIRST_EX_LARGE_FETCH);
104
105 if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
106 // Use a "large fetch" on newer Windows which should speed up large
107 // enumerations (we seldom abort in the middle).
108 find_handle_ = FindFirstFileEx(src.value().c_str(),
109 FindExInfoBasic, // Omit short name.
110 &find_data_,
111 FindExSearchNameMatch,
112 NULL,
113 FIND_FIRST_EX_LARGE_FETCH);
114 } else {
115 find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
116 }
117 has_find_data_ = true; 130 has_find_data_ = true;
118 } else { 131 } else {
119 // Search for the next file/directory. 132 // Search for the next file/directory.
120 if (!FindNextFile(find_handle_, &find_data_)) { 133 if (!FindNextFile(find_handle_, &find_data_)) {
121 FindClose(find_handle_); 134 FindClose(find_handle_);
122 find_handle_ = INVALID_HANDLE_VALUE; 135 find_handle_ = INVALID_HANDLE_VALUE;
123 } 136 }
124 } 137 }
125 138
126 if (INVALID_HANDLE_VALUE == find_handle_) { 139 if (INVALID_HANDLE_VALUE == find_handle_) {
127 has_find_data_ = false; 140 has_find_data_ = false;
128 141
129 // This is reached when we have finished a directory and are advancing to 142 // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
130 // the next one in the queue. We applied the pattern (if any) to the files 143 // applies pattern for all subfolders.
131 // in the root search directory, but for those directories which were 144 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
132 // matched, we want to enumerate all files inside them. This will happen 145 // This is reached when we have finished a directory and are advancing
133 // when the handle is empty. 146 // to the next one in the queue. We applied the pattern (if any) to the
134 pattern_ = FilePath::StringType(); 147 // files in the root search directory, but for those directories which
148 // were matched, we want to enumerate all files inside them. This will
149 // happen when the handle is empty.
150 pattern_ = FILE_PATH_LITERAL("*");
Lei Zhang 2017/06/08 08:21:18 You don't need FILE_PATH_LITERAL in Windows-only f
ivafanas 2017/06/16 11:13:16 Done.
151 }
135 152
136 continue; 153 continue;
137 } 154 }
138 155
139 FilePath cur_file(find_data_.cFileName); 156 const FilePath filename(find_data_.cFileName);
140 if (ShouldSkip(cur_file)) 157 if (ShouldSkip(filename))
141 continue; 158 continue;
142 159
143 // Construct the absolute filename. 160 const bool is_dir =
144 cur_file = root_path_.Append(find_data_.cFileName); 161 (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
162 const auto abs_path = root_path_.Append(filename);
Lei Zhang 2017/06/08 08:21:19 Just write out FilePath. Using auto is not saving
ivafanas 2017/06/16 11:13:16 Done.
145 163
146 if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 164 // Check if directory should be processed recursive.
147 if (recursive_) { 165 if (is_dir && recursive_) {
148 // If |cur_file| is a directory, and we are doing recursive searching, 166 // If |cur_file| is a directory, and we are doing recursive searching,
149 // add it to pending_paths_ so we scan it after we finish scanning this 167 // add it to pending_paths_ so we scan it after we finish scanning this
150 // directory. However, don't do recursion through reparse points or we 168 // directory. However, don't do recursion through reparse points or we
151 // may end up with an infinite cycle. 169 // may end up with an infinite cycle.
152 DWORD attributes = GetFileAttributes(cur_file.value().c_str()); 170 DWORD attributes = GetFileAttributes(abs_path.value().c_str());
153 if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) 171 if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
154 pending_paths_.push(cur_file); 172 pending_paths_.push(abs_path);
155 }
156 if (file_type_ & FileEnumerator::DIRECTORIES)
157 return cur_file;
158 } else if (file_type_ & FileEnumerator::FILES) {
159 return cur_file;
160 } 173 }
174
175 if (IsTypeMatched(is_dir) && IsPatternMatched(filename))
176 return abs_path;
161 } 177 }
162
163 return FilePath(); 178 return FilePath();
164 } 179 }
165 180
181 bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
182 switch (folder_search_policy_) {
183 case FolderSearchPolicy::MATCH_ONLY:
184 // MATCH_ONLY policy filters by pattern on search request, so all found
185 // files already fits to pattern.
186 return true;
187 case FolderSearchPolicy::ALL:
188 // ALL policy enumerates all files, we need to check pattern match
189 // manually.
190 return pattern_.empty() ||
Lei Zhang 2017/06/08 08:21:19 Can |pattern_| be empty?
ivafanas 2017/06/16 11:13:15 You are right, it can not. Fixed.
191 PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE;
192 }
193 NOTREACHED();
194 return false;
195 }
196
166 } // namespace base 197 } // namespace base
OLDNEW
« base/files/file_enumerator_unittest.cc ('K') | « base/files/file_enumerator_unittest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698