Chromium Code Reviews| 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 #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("*"); | |
| 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 Loading... | |
| 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 Loading... | |
| 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 auto src = |
|
Lei Zhang
2017/06/01 01:37:29
I think you should just write out FilePath here in
ivafanas
2017/06/01 08:29:19
Done.
| |
| 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. |
| 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("*"); | |
| 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); | |
| 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() || | |
| 191 PathMatchSpec(src.value().c_str(), pattern_.c_str()) == TRUE; | |
| 192 } | |
| 193 NOTREACHED(); | |
| 194 return false; | |
| 195 } | |
| 196 | |
| 166 } // namespace base | 197 } // namespace base |
| OLD | NEW |