• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 #ifndef BASE_FILES_FILE_ENUMERATOR_H_
6 #define BASE_FILES_FILE_ENUMERATOR_H_
7 
8 #include <stddef.h>
9 #include <stdint.h>
10 
11 #include <vector>
12 
13 #include "base/base_export.h"
14 #include "base/containers/stack.h"
15 #include "base/files/file.h"
16 #include "base/files/file_path.h"
17 #include "base/functional/function_ref.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 
21 #if BUILDFLAG(IS_WIN)
22 #include "base/win/windows_types.h"
23 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
24 #include <sys/stat.h>
25 #include <unistd.h>
26 
27 #include <unordered_map>
28 #include <unordered_set>
29 #endif
30 
31 namespace base {
32 
33 // A class for enumerating the files in a provided path. The order of the
34 // results is not guaranteed.
35 //
36 // This is blocking. Do not use on critical threads.
37 //
38 // Example:
39 //
40 //   base::FileEnumerator e(my_dir, false, base::FileEnumerator::FILES,
41 //                          FILE_PATH_LITERAL("*.txt"));
42 // Using `ForEach` with a lambda:
43 //   e.ForEach([](const base::FilePath& item) {...});
44 // Using a `for` loop:
45 //   for (base::FilePath name = e.Next(); !name.empty(); name = e.Next())
46 //     ...
47 class BASE_EXPORT FileEnumerator {
48  public:
49   // Note: copy & assign supported.
50   class BASE_EXPORT FileInfo {
51    public:
52     FileInfo();
53 #if BUILDFLAG(IS_ANDROID)
54     // Android has both posix paths, and Content-URIs. It will use the linux /
55     // posix code for posix paths where a FileInfo() object is constructed and
56     // then `stat_` is populated via fstat() and used for IsDirectory(),
57     // GetSize(), GetLastModifiedTime(). Content-URIs provide all values in this
58     // constructor and writes `is_directory`, `size` and `time` to `stat_`.
59     FileInfo(base::FilePath content_uri,
60              base::FilePath filename,
61              bool is_directory,
62              off_t size,
63              Time time);
64 #endif
65     FileInfo(const FileInfo& that);
66     FileInfo& operator=(const FileInfo& that);
67     FileInfo(FileInfo&& that);
68     FileInfo& operator=(FileInfo&& that);
69     ~FileInfo();
70 
71     bool IsDirectory() const;
72 
73     // The name of the file. This will not include any path information. This
74     // is in constrast to the value returned by FileEnumerator.Next() which
75     // includes the |root_path| passed into the FileEnumerator constructor.
76     FilePath GetName() const;
77 
78 #if BUILDFLAG(IS_ANDROID)
79     // Display names of subdirs.
subdirs()80     const std::vector<std::string>& subdirs() const { return subdirs_; }
81 #endif
82 
83     int64_t GetSize() const;
84 
85     // On POSIX systems, this is rounded down to the second.
86     Time GetLastModifiedTime() const;
87 
88 #if BUILDFLAG(IS_WIN)
89     // Note that the cAlternateFileName (used to hold the "short" 8.3 name)
90     // of the WIN32_FIND_DATA will be empty. Since we don't use short file
91     // names, we tell Windows to omit it which speeds up the query slightly.
find_data()92     const WIN32_FIND_DATA& find_data() const {
93       return *ChromeToWindowsType(&find_data_);
94     }
95 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
stat()96     const stat_wrapper_t& stat() const { return stat_; }
97 #endif
98 
99    private:
100     friend class FileEnumerator;
101 
102 #if BUILDFLAG(IS_ANDROID)
103     FilePath content_uri_;
104     std::vector<std::string> subdirs_;
105 #endif
106 #if BUILDFLAG(IS_WIN)
107     CHROME_WIN32_FIND_DATA find_data_;
108 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
109     stat_wrapper_t stat_;
110     FilePath filename_;
111 #endif
112   };
113 
114   enum FileType {
115     FILES = 1 << 0,
116     DIRECTORIES = 1 << 1,
117     INCLUDE_DOT_DOT = 1 << 2,
118 
119     // Report only the names of entries and not their type, size, or
120     // last-modified time. May only be used for non-recursive enumerations, and
121     // implicitly includes both files and directories (neither of which may be
122     // specified). When used, an enumerator's `GetInfo()` method must not be
123     // called.
124     NAMES_ONLY = 1 << 3,
125 
126 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
127     SHOW_SYM_LINKS = 1 << 4,
128 #endif
129   };
130 
131   // Search policy for intermediate folders.
132   enum class FolderSearchPolicy {
133     // Recursive search will pass through folders whose names match the
134     // pattern. Inside each one, all files will be returned. Folders with names
135     // that do not match the pattern will be ignored within their interior.
136     MATCH_ONLY,
137     // Recursive search will pass through every folder and perform pattern
138     // matching inside each one.
139     ALL,
140   };
141 
142   // Determines how a FileEnumerator handles errors encountered during
143   // enumeration. When no ErrorPolicy is explicitly set, FileEnumerator defaults
144   // to IGNORE_ERRORS.
145   enum class ErrorPolicy {
146     // Errors are ignored if possible and FileEnumerator returns as many files
147     // as it is able to enumerate.
148     IGNORE_ERRORS,
149 
150     // Any error encountered during enumeration will terminate the enumeration
151     // immediately. An error code indicating the nature of a failure can be
152     // retrieved from |GetError()|.
153     STOP_ENUMERATION,
154   };
155 
156   // |root_path| is the starting directory to search for. It may or may not end
157   // in a slash.
158   //
159   // If |recursive| is true, this will enumerate all matches in any
160   // subdirectories matched as well. It does a breadth-first search, so all
161   // files in one directory will be returned before any files in a
162   // subdirectory.
163   //
164   // |file_type|, a bit mask of FileType, specifies whether the enumerator
165   // should match files, directories, or both.
166   //
167   // |pattern| is an optional pattern for which files to match. This
168   // works like shell globbing. For example, "*.txt" or "Foo???.doc".
169   // However, be careful in specifying patterns that aren't cross platform
170   // since the underlying code uses OS-specific matching routines.  In general,
171   // Windows matching is less featureful than others, so test there first.
172   // If unspecified, this will match all files.
173   //
174   // |folder_search_policy| optionally specifies a search behavior. Refer to
175   // |FolderSearchPolicy| for a list of folder search policies and the meaning
176   // of them. If |recursive| is false, this parameter has no effect.
177   //
178   // |error_policy| optionally specifies the behavior when an error occurs.
179   // Refer to |ErrorPolicy| for a list of error policies and the meaning of
180   // them.
181   FileEnumerator(const FilePath& root_path, bool recursive, int file_type);
182   FileEnumerator(const FilePath& root_path,
183                  bool recursive,
184                  int file_type,
185                  const FilePath::StringType& pattern);
186   FileEnumerator(const FilePath& root_path,
187                  bool recursive,
188                  int file_type,
189                  const FilePath::StringType& pattern,
190                  FolderSearchPolicy folder_search_policy);
191   FileEnumerator(const FilePath& root_path,
192                  bool recursive,
193                  int file_type,
194                  const FilePath::StringType& pattern,
195                  FolderSearchPolicy folder_search_policy,
196                  ErrorPolicy error_policy);
197   FileEnumerator(const FileEnumerator&) = delete;
198   FileEnumerator& operator=(const FileEnumerator&) = delete;
199   ~FileEnumerator();
200 
201   // Calls `ref` synchronously for each path found by the `FileEnumerator`. Each
202   // path will incorporate the `root_path` passed in the constructor:
203   // "<root_path>/file_name.txt". If the `root_path` is absolute, then so will
204   // be the paths provided in the `ref` invocations.
205   void ForEach(FunctionRef<void(const FilePath& path)> ref);
206 
207   // Returns the next file or an empty string if there are no more results.
208   //
209   // The returned path will incorporate the |root_path| passed in the
210   // constructor: "<root_path>/file_name.txt". If the |root_path| is absolute,
211   // then so will be the result of Next().
212   FilePath Next();
213 
214   // Returns info about the file last returned by Next(). Note that on Windows
215   // and Fuchsia, GetInfo() does not play well with INCLUDE_DOT_DOT. In
216   // particular, the GetLastModifiedTime() for the .. directory is 1601-01-01
217   // on Fuchsia (https://crbug.com/1106172) and is equal to the last modified
218   // time of the current directory on Windows (https://crbug.com/1119546).
219   // Must not be used with FileType::NAMES_ONLY.
220   FileInfo GetInfo() const;
221 
222   // Once |Next()| returns an empty path, enumeration has been terminated. If
223   // termination was normal (i.e. no more results to enumerate) or ErrorPolicy
224   // is set to IGNORE_ERRORS, this returns FILE_OK. Otherwise it returns an
225   // error code reflecting why enumeration was stopped early.
GetError()226   File::Error GetError() const { return error_; }
227 
228  private:
229   // Returns true if the given path should be skipped in enumeration.
230   bool ShouldSkip(const FilePath& path);
231 
232   bool IsTypeMatched(bool is_dir) const;
233 
234   bool IsPatternMatched(const FilePath& src) const;
235 
236 #if BUILDFLAG(IS_WIN)
find_data()237   const WIN32_FIND_DATA& find_data() const {
238     return *ChromeToWindowsType(&find_data_);
239   }
240 
241   // True when find_data_ is valid.
242   bool has_find_data_ = false;
243   CHROME_WIN32_FIND_DATA find_data_;
244   HANDLE find_handle_ = INVALID_HANDLE_VALUE;
245 
246 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
247   // Marks the given inode as visited. Returns true if it is the first time that
248   // it got marked as visited.
MarkVisited(const stat_wrapper_t & st)249   bool MarkVisited(const stat_wrapper_t& st) {
250     return visited_[st.st_dev].insert(st.st_ino).second;
251   }
252 
253   // The files in the current directory
254   std::vector<FileInfo> directory_entries_;
255 
256 #if BUILDFLAG(IS_ANDROID)
257   // The Android NDK (r23) does not declare `st_dev` as a `dev_t`, nor `st_ino`
258   // as an `ino_t`, hence the need for these decltypes.
259   using dev_t = decltype(stat_wrapper_t::st_dev);
260   using ino_t = decltype(stat_wrapper_t::st_ino);
261 #endif
262 
263   // Set of visited directories. Used to prevent infinite looping along circular
264   // symlinks and bind-mounts.
265   std::unordered_map<dev_t, std::unordered_set<ino_t>> visited_;
266 
267   // The next entry to use from the directory_entries_ vector
268   size_t current_directory_entry_;
269 #endif
270   FilePath root_path_;
271   const bool recursive_;
272   int file_type_;
273   FilePath::StringType pattern_;
274   const FolderSearchPolicy folder_search_policy_;
275   const ErrorPolicy error_policy_;
276   File::Error error_ = File::FILE_OK;
277 
278   // A stack that keeps track of which subdirectories we still need to
279   // enumerate in the breadth-first search.
280   base::stack<FilePath> pending_paths_;
281 #if BUILDFLAG(IS_ANDROID)
282   // Matches pending_paths_, but with display names.
283   base::stack<std::vector<std::string>> pending_subdirs_;
284   // Display names of subdirs of the current entry.
285   std::vector<std::string> subdirs_;
286 #endif
287 };
288 
289 }  // namespace base
290 
291 #endif  // BASE_FILES_FILE_ENUMERATOR_H_
292