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