OLD | NEW |
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 | 13 |
13 namespace base { | 14 namespace base { |
14 | 15 |
| 16 namespace { |
| 17 |
| 18 FilePath BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy, |
| 19 const FilePath& root_path, |
| 20 const FilePath::StringType& pattern) { |
| 21 // MATCH_ONLY policy filters incoming files by pattern on OS side. ALL policy |
| 22 // collects all files and filters them manually. |
| 23 switch (policy) { |
| 24 case FileEnumerator::FolderSearchPolicy::MATCH_ONLY: |
| 25 return root_path.Append(pattern); |
| 26 case FileEnumerator::FolderSearchPolicy::ALL: |
| 27 return root_path.Append(L"*"); |
| 28 } |
| 29 NOTREACHED(); |
| 30 return {}; |
| 31 } |
| 32 |
| 33 } // namespace |
| 34 |
15 // FileEnumerator::FileInfo ---------------------------------------------------- | 35 // FileEnumerator::FileInfo ---------------------------------------------------- |
16 | 36 |
17 FileEnumerator::FileInfo::FileInfo() { | 37 FileEnumerator::FileInfo::FileInfo() { |
18 memset(&find_data_, 0, sizeof(find_data_)); | 38 memset(&find_data_, 0, sizeof(find_data_)); |
19 } | 39 } |
20 | 40 |
21 bool FileEnumerator::FileInfo::IsDirectory() const { | 41 bool FileEnumerator::FileInfo::IsDirectory() const { |
22 return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | 42 return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; |
23 } | 43 } |
24 | 44 |
(...skipping 12 matching lines...) Expand all Loading... |
37 | 57 |
38 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { | 58 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { |
39 return base::Time::FromFileTime(find_data_.ftLastWriteTime); | 59 return base::Time::FromFileTime(find_data_.ftLastWriteTime); |
40 } | 60 } |
41 | 61 |
42 // FileEnumerator -------------------------------------------------------------- | 62 // FileEnumerator -------------------------------------------------------------- |
43 | 63 |
44 FileEnumerator::FileEnumerator(const FilePath& root_path, | 64 FileEnumerator::FileEnumerator(const FilePath& root_path, |
45 bool recursive, | 65 bool recursive, |
46 int file_type) | 66 int file_type) |
47 : has_find_data_(false), | 67 : FileEnumerator(root_path, |
48 find_handle_(INVALID_HANDLE_VALUE), | 68 recursive, |
49 recursive_(recursive), | 69 file_type, |
50 file_type_(file_type) { | 70 FilePath::StringType(), |
51 // INCLUDE_DOT_DOT must not be specified if recursive. | 71 FolderSearchPolicy::MATCH_ONLY) {} |
52 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | |
53 memset(&find_data_, 0, sizeof(find_data_)); | |
54 pending_paths_.push(root_path); | |
55 } | |
56 | 72 |
57 FileEnumerator::FileEnumerator(const FilePath& root_path, | 73 FileEnumerator::FileEnumerator(const FilePath& root_path, |
58 bool recursive, | 74 bool recursive, |
59 int file_type, | 75 int file_type, |
60 const FilePath::StringType& pattern) | 76 const FilePath::StringType& pattern) |
61 : has_find_data_(false), | 77 : FileEnumerator(root_path, |
62 find_handle_(INVALID_HANDLE_VALUE), | 78 recursive, |
63 recursive_(recursive), | 79 file_type, |
| 80 pattern, |
| 81 FolderSearchPolicy::MATCH_ONLY) {} |
| 82 |
| 83 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 84 bool recursive, |
| 85 int file_type, |
| 86 const FilePath::StringType& pattern, |
| 87 FolderSearchPolicy folder_search_policy) |
| 88 : recursive_(recursive), |
64 file_type_(file_type), | 89 file_type_(file_type), |
65 pattern_(pattern) { | 90 pattern_(!pattern.empty() ? pattern : L"*"), |
| 91 folder_search_policy_(folder_search_policy) { |
66 // INCLUDE_DOT_DOT must not be specified if recursive. | 92 // INCLUDE_DOT_DOT must not be specified if recursive. |
67 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | 93 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
68 memset(&find_data_, 0, sizeof(find_data_)); | 94 memset(&find_data_, 0, sizeof(find_data_)); |
69 pending_paths_.push(root_path); | 95 pending_paths_.push(root_path); |
70 } | 96 } |
71 | 97 |
72 FileEnumerator::~FileEnumerator() { | 98 FileEnumerator::~FileEnumerator() { |
73 if (find_handle_ != INVALID_HANDLE_VALUE) | 99 if (find_handle_ != INVALID_HANDLE_VALUE) |
74 FindClose(find_handle_); | 100 FindClose(find_handle_); |
75 } | 101 } |
(...skipping 11 matching lines...) Expand all Loading... |
87 FilePath FileEnumerator::Next() { | 113 FilePath FileEnumerator::Next() { |
88 base::ThreadRestrictions::AssertIOAllowed(); | 114 base::ThreadRestrictions::AssertIOAllowed(); |
89 | 115 |
90 while (has_find_data_ || !pending_paths_.empty()) { | 116 while (has_find_data_ || !pending_paths_.empty()) { |
91 if (!has_find_data_) { | 117 if (!has_find_data_) { |
92 // The last find FindFirstFile operation is done, prepare a new one. | 118 // The last find FindFirstFile operation is done, prepare a new one. |
93 root_path_ = pending_paths_.top(); | 119 root_path_ = pending_paths_.top(); |
94 pending_paths_.pop(); | 120 pending_paths_.pop(); |
95 | 121 |
96 // Start a new find operation. | 122 // Start a new find operation. |
97 FilePath src = root_path_; | 123 const FilePath src = |
98 | 124 BuildSearchFilter(folder_search_policy_, root_path_, pattern_); |
99 if (pattern_.empty()) | |
100 src = src.Append(L"*"); // No pattern = match everything. | |
101 else | |
102 src = src.Append(pattern_); | |
103 | |
104 // Use a "large fetch" which should speed up large enumerations (we seldom | |
105 // abort in the middle). | |
106 find_handle_ = FindFirstFileEx(src.value().c_str(), | 125 find_handle_ = FindFirstFileEx(src.value().c_str(), |
107 FindExInfoBasic, // Omit short name. | 126 FindExInfoBasic, // Omit short name. |
108 &find_data_, | 127 &find_data_, FindExSearchNameMatch, |
109 FindExSearchNameMatch, | 128 nullptr, FIND_FIRST_EX_LARGE_FETCH); |
110 NULL, | |
111 FIND_FIRST_EX_LARGE_FETCH); | |
112 has_find_data_ = true; | 129 has_find_data_ = true; |
113 } else { | 130 } else { |
114 // Search for the next file/directory. | 131 // Search for the next file/directory. |
115 if (!FindNextFile(find_handle_, &find_data_)) { | 132 if (!FindNextFile(find_handle_, &find_data_)) { |
116 FindClose(find_handle_); | 133 FindClose(find_handle_); |
117 find_handle_ = INVALID_HANDLE_VALUE; | 134 find_handle_ = INVALID_HANDLE_VALUE; |
118 } | 135 } |
119 } | 136 } |
120 | 137 |
121 if (INVALID_HANDLE_VALUE == find_handle_) { | 138 if (INVALID_HANDLE_VALUE == find_handle_) { |
122 has_find_data_ = false; | 139 has_find_data_ = false; |
123 | 140 |
124 // This is reached when we have finished a directory and are advancing to | 141 // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy |
125 // the next one in the queue. We applied the pattern (if any) to the files | 142 // applies pattern for all subfolders. |
126 // in the root search directory, but for those directories which were | 143 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) { |
127 // matched, we want to enumerate all files inside them. This will happen | 144 // This is reached when we have finished a directory and are advancing |
128 // when the handle is empty. | 145 // to the next one in the queue. We applied the pattern (if any) to the |
129 pattern_ = FilePath::StringType(); | 146 // files in the root search directory, but for those directories which |
| 147 // were matched, we want to enumerate all files inside them. This will |
| 148 // happen when the handle is empty. |
| 149 pattern_ = L"*"; |
| 150 } |
130 | 151 |
131 continue; | 152 continue; |
132 } | 153 } |
133 | 154 |
134 FilePath cur_file(find_data_.cFileName); | 155 const FilePath filename(find_data_.cFileName); |
135 if (ShouldSkip(cur_file)) | 156 if (ShouldSkip(filename)) |
136 continue; | 157 continue; |
137 | 158 |
138 // Construct the absolute filename. | 159 const bool is_dir = |
139 cur_file = root_path_.Append(find_data_.cFileName); | 160 (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; |
| 161 const FilePath abs_path = root_path_.Append(filename); |
140 | 162 |
141 if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | 163 // Check if directory should be processed recursive. |
142 if (recursive_) { | 164 if (is_dir && recursive_) { |
143 // If |cur_file| is a directory, and we are doing recursive searching, | 165 // If |cur_file| is a directory, and we are doing recursive searching, |
144 // add it to pending_paths_ so we scan it after we finish scanning this | 166 // add it to pending_paths_ so we scan it after we finish scanning this |
145 // directory. However, don't do recursion through reparse points or we | 167 // directory. However, don't do recursion through reparse points or we |
146 // may end up with an infinite cycle. | 168 // may end up with an infinite cycle. |
147 DWORD attributes = GetFileAttributes(cur_file.value().c_str()); | 169 DWORD attributes = GetFileAttributes(abs_path.value().c_str()); |
148 if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) | 170 if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT)) |
149 pending_paths_.push(cur_file); | 171 pending_paths_.push(abs_path); |
150 } | |
151 if (file_type_ & FileEnumerator::DIRECTORIES) | |
152 return cur_file; | |
153 } else if (file_type_ & FileEnumerator::FILES) { | |
154 return cur_file; | |
155 } | 172 } |
| 173 |
| 174 if (IsTypeMatched(is_dir) && IsPatternMatched(filename)) |
| 175 return abs_path; |
156 } | 176 } |
157 | |
158 return FilePath(); | 177 return FilePath(); |
159 } | 178 } |
160 | 179 |
| 180 bool FileEnumerator::IsPatternMatched(const FilePath& src) const { |
| 181 switch (folder_search_policy_) { |
| 182 case FolderSearchPolicy::MATCH_ONLY: |
| 183 // MATCH_ONLY policy filters by pattern on search request, so all found |
| 184 // files already fits to pattern. |
| 185 return true; |
| 186 case FolderSearchPolicy::ALL: |
| 187 // ALL policy enumerates all files, we need to check pattern match |
| 188 // manually. |
| 189 return PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE; |
| 190 } |
| 191 NOTREACHED(); |
| 192 return false; |
| 193 } |
| 194 |
161 } // namespace base | 195 } // namespace base |
OLD | NEW |