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