• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "base/files/file_enumerator.h"
6 
7 #include <shlwapi.h>
8 #include <stdint.h>
9 #include <string.h>
10 
11 #include "base/logging.h"
12 #include "base/win/win_util.h"
13 
14 namespace base {
15 
16 namespace {
17 
BuildSearchFilter(FileEnumerator::FolderSearchPolicy policy,const FilePath & root_path,const FilePath::StringType & pattern)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(u"*");
28   }
29   NOTREACHED();
30   return {};
31 }
32 
33 }  // namespace
34 
35 // FileEnumerator::FileInfo ----------------------------------------------------
36 
FileInfo()37 FileEnumerator::FileInfo::FileInfo() {
38   memset(&find_data_, 0, sizeof(find_data_));
39 }
40 
IsDirectory() const41 bool FileEnumerator::FileInfo::IsDirectory() const {
42   return (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
43 }
44 
GetName() const45 FilePath FileEnumerator::FileInfo::GetName() const {
46   return FilePath(reinterpret_cast<const char16_t*>(find_data_.cFileName));
47 }
48 
GetSize() const49 int64_t FileEnumerator::FileInfo::GetSize() const {
50   ULARGE_INTEGER size;
51   size.HighPart = find_data_.nFileSizeHigh;
52   size.LowPart = find_data_.nFileSizeLow;
53   DCHECK_LE(size.QuadPart,
54             static_cast<ULONGLONG>(std::numeric_limits<int64_t>::max()));
55   return static_cast<int64_t>(size.QuadPart);
56 }
57 
GetLastModifiedTime() const58 Ticks FileEnumerator::FileInfo::GetLastModifiedTime() const {
59   return *reinterpret_cast<const uint64_t*>(&find_data_.ftLastWriteTime);
60 }
61 
62 // FileEnumerator --------------------------------------------------------------
63 
FileEnumerator(const FilePath & root_path,bool recursive,int file_type)64 FileEnumerator::FileEnumerator(const FilePath& root_path,
65                                bool recursive,
66                                int file_type)
67     : FileEnumerator(root_path,
68                      recursive,
69                      file_type,
70                      FilePath::StringType(),
71                      FolderSearchPolicy::MATCH_ONLY) {}
72 
FileEnumerator(const FilePath & root_path,bool recursive,int file_type,const FilePath::StringType & pattern)73 FileEnumerator::FileEnumerator(const FilePath& root_path,
74                                bool recursive,
75                                int file_type,
76                                const FilePath::StringType& pattern)
77     : FileEnumerator(root_path,
78                      recursive,
79                      file_type,
80                      pattern,
81                      FolderSearchPolicy::MATCH_ONLY) {}
82 
FileEnumerator(const FilePath & root_path,bool recursive,int file_type,const FilePath::StringType & pattern,FolderSearchPolicy folder_search_policy)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),
89       file_type_(file_type),
90       pattern_(!pattern.empty() ? pattern : u"*"),
91       folder_search_policy_(folder_search_policy) {
92   // INCLUDE_DOT_DOT must not be specified if recursive.
93   DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
94   memset(&find_data_, 0, sizeof(find_data_));
95   pending_paths_.push(root_path);
96 }
97 
~FileEnumerator()98 FileEnumerator::~FileEnumerator() {
99   if (find_handle_ != INVALID_HANDLE_VALUE)
100     FindClose(find_handle_);
101 }
102 
GetInfo() const103 FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
104   if (!has_find_data_) {
105     NOTREACHED();
106     return FileInfo();
107   }
108   FileInfo ret;
109   memcpy(&ret.find_data_, &find_data_, sizeof(find_data_));
110   return ret;
111 }
112 
Next()113 FilePath FileEnumerator::Next() {
114   while (has_find_data_ || !pending_paths_.empty()) {
115     if (!has_find_data_) {
116       // The last find FindFirstFile operation is done, prepare a new one.
117       root_path_ = pending_paths_.top();
118       pending_paths_.pop();
119 
120       // Start a new find operation.
121       const FilePath src =
122           BuildSearchFilter(folder_search_policy_, root_path_, pattern_);
123       find_handle_ = FindFirstFileEx(ToWCharT(&src.value()),
124                                      FindExInfoBasic,  // Omit short name.
125                                      &find_data_, FindExSearchNameMatch,
126                                      nullptr, FIND_FIRST_EX_LARGE_FETCH);
127       has_find_data_ = true;
128     } else {
129       // Search for the next file/directory.
130       if (!FindNextFile(find_handle_, &find_data_)) {
131         FindClose(find_handle_);
132         find_handle_ = INVALID_HANDLE_VALUE;
133       }
134     }
135 
136     if (INVALID_HANDLE_VALUE == find_handle_) {
137       has_find_data_ = false;
138 
139       // MATCH_ONLY policy clears pattern for matched subfolders. ALL policy
140       // applies pattern for all subfolders.
141       if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) {
142         // This is reached when we have finished a directory and are advancing
143         // to the next one in the queue. We applied the pattern (if any) to the
144         // files in the root search directory, but for those directories which
145         // were matched, we want to enumerate all files inside them. This will
146         // happen when the handle is empty.
147         pattern_ = u"*";
148       }
149 
150       continue;
151     }
152 
153     const FilePath filename(reinterpret_cast<char16_t*>(find_data_.cFileName));
154     if (ShouldSkip(filename))
155       continue;
156 
157     const bool is_dir =
158         (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
159     const FilePath abs_path = root_path_.Append(filename);
160 
161     // Check if directory should be processed recursive.
162     if (is_dir && recursive_) {
163       // If |cur_file| is a directory, and we are doing recursive searching,
164       // add it to pending_paths_ so we scan it after we finish scanning this
165       // directory. However, don't do recursion through reparse points or we
166       // may end up with an infinite cycle.
167       DWORD attributes = GetFileAttributes(ToWCharT(&abs_path.value()));
168       if (!(attributes & FILE_ATTRIBUTE_REPARSE_POINT))
169         pending_paths_.push(abs_path);
170     }
171 
172     if (IsTypeMatched(is_dir) && IsPatternMatched(filename))
173       return abs_path;
174   }
175   return FilePath();
176 }
177 
IsPatternMatched(const FilePath & src) const178 bool FileEnumerator::IsPatternMatched(const FilePath& src) const {
179   switch (folder_search_policy_) {
180     case FolderSearchPolicy::MATCH_ONLY:
181       // MATCH_ONLY policy filters by pattern on search request, so all found
182       // files already fits to pattern.
183       return true;
184     case FolderSearchPolicy::ALL:
185       // ALL policy enumerates all files, we need to check pattern match
186       // manually.
187       return PathMatchSpec(ToWCharT(&src.value()), ToWCharT(&pattern_)) == TRUE;
188   }
189   NOTREACHED();
190   return false;
191 }
192 
193 }  // namespace base
194