file_enumerator_posix.cc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. #include "base/files/file_enumerator.h"
  5. #include <dirent.h>
  6. #include <errno.h>
  7. #include <fnmatch.h>
  8. #include <stdint.h>
  9. #include <string.h>
  10. #include "base/logging.h"
  11. #include "base/threading/scoped_blocking_call.h"
  12. #include "build/build_config.h"
  13. namespace base {
  14. namespace {
  15. void GetStat(const FilePath& path, bool show_links, stat_wrapper_t* st) {
  16. DCHECK(st);
  17. const int res = show_links ? File::Lstat(path.value().c_str(), st)
  18. : File::Stat(path.value().c_str(), st);
  19. if (res < 0) {
  20. // Print the stat() error message unless it was ENOENT and we're following
  21. // symlinks.
  22. DPLOG_IF(ERROR, errno != ENOENT || show_links)
  23. << "Cannot stat '" << path << "'";
  24. memset(st, 0, sizeof(*st));
  25. }
  26. }
  27. #if BUILDFLAG(IS_FUCHSIA)
  28. bool ShouldShowSymLinks(int file_type) {
  29. return false;
  30. }
  31. #else
  32. bool ShouldShowSymLinks(int file_type) {
  33. return file_type & FileEnumerator::SHOW_SYM_LINKS;
  34. }
  35. #endif // BUILDFLAG(IS_FUCHSIA)
  36. #if BUILDFLAG(IS_FUCHSIA)
  37. bool ShouldTrackVisitedDirectories(int file_type) {
  38. return false;
  39. }
  40. #else
  41. bool ShouldTrackVisitedDirectories(int file_type) {
  42. return !(file_type & FileEnumerator::SHOW_SYM_LINKS);
  43. }
  44. #endif // BUILDFLAG(IS_FUCHSIA)
  45. } // namespace
  46. // FileEnumerator::FileInfo ----------------------------------------------------
  47. FileEnumerator::FileInfo::FileInfo() {
  48. memset(&stat_, 0, sizeof(stat_));
  49. }
  50. bool FileEnumerator::FileInfo::IsDirectory() const {
  51. return S_ISDIR(stat_.st_mode);
  52. }
  53. FilePath FileEnumerator::FileInfo::GetName() const {
  54. return filename_;
  55. }
  56. int64_t FileEnumerator::FileInfo::GetSize() const {
  57. return stat_.st_size;
  58. }
  59. base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const {
  60. return base::Time::FromTimeT(stat_.st_mtime);
  61. }
  62. // FileEnumerator --------------------------------------------------------------
  63. FileEnumerator::FileEnumerator(const FilePath& root_path,
  64. bool recursive,
  65. int file_type)
  66. : FileEnumerator(root_path,
  67. recursive,
  68. file_type,
  69. FilePath::StringType(),
  70. FolderSearchPolicy::MATCH_ONLY) {}
  71. FileEnumerator::FileEnumerator(const FilePath& root_path,
  72. bool recursive,
  73. int file_type,
  74. const FilePath::StringType& pattern)
  75. : FileEnumerator(root_path,
  76. recursive,
  77. file_type,
  78. pattern,
  79. FolderSearchPolicy::MATCH_ONLY) {}
  80. FileEnumerator::FileEnumerator(const FilePath& root_path,
  81. bool recursive,
  82. int file_type,
  83. const FilePath::StringType& pattern,
  84. FolderSearchPolicy folder_search_policy)
  85. : FileEnumerator(root_path,
  86. recursive,
  87. file_type,
  88. pattern,
  89. folder_search_policy,
  90. ErrorPolicy::IGNORE_ERRORS) {}
  91. FileEnumerator::FileEnumerator(const FilePath& root_path,
  92. bool recursive,
  93. int file_type,
  94. const FilePath::StringType& pattern,
  95. FolderSearchPolicy folder_search_policy,
  96. ErrorPolicy error_policy)
  97. : current_directory_entry_(0),
  98. root_path_(root_path),
  99. recursive_(recursive),
  100. file_type_(file_type),
  101. pattern_(pattern),
  102. folder_search_policy_(folder_search_policy),
  103. error_policy_(error_policy) {
  104. // INCLUDE_DOT_DOT must not be specified if recursive.
  105. DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_)));
  106. if (recursive && ShouldTrackVisitedDirectories(file_type_)) {
  107. stat_wrapper_t st;
  108. GetStat(root_path, false, &st);
  109. visited_directories_.insert(st.st_ino);
  110. }
  111. pending_paths_.push(root_path);
  112. }
  113. FileEnumerator::~FileEnumerator() = default;
  114. FilePath FileEnumerator::Next() {
  115. ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
  116. ++current_directory_entry_;
  117. // While we've exhausted the entries in the current directory, do the next
  118. while (current_directory_entry_ >= directory_entries_.size()) {
  119. if (pending_paths_.empty())
  120. return FilePath();
  121. root_path_ = pending_paths_.top();
  122. root_path_ = root_path_.StripTrailingSeparators();
  123. pending_paths_.pop();
  124. DIR* dir = opendir(root_path_.value().c_str());
  125. if (!dir) {
  126. if (errno == 0 || error_policy_ == ErrorPolicy::IGNORE_ERRORS)
  127. continue;
  128. error_ = File::OSErrorToFileError(errno);
  129. return FilePath();
  130. }
  131. directory_entries_.clear();
  132. #if BUILDFLAG(IS_FUCHSIA)
  133. // Fuchsia does not support .. on the file system server side, see
  134. // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and
  135. // https://crbug.com/735540. However, for UI purposes, having the parent
  136. // directory show up in directory listings makes sense, so we add it here to
  137. // match the expectation on other operating systems. In cases where this
  138. // is useful it should be resolvable locally.
  139. FileInfo dotdot;
  140. dotdot.stat_.st_mode = S_IFDIR;
  141. dotdot.filename_ = FilePath("..");
  142. if (!ShouldSkip(dotdot.filename_)) {
  143. directory_entries_.push_back(std::move(dotdot));
  144. }
  145. #endif // BUILDFLAG(IS_FUCHSIA)
  146. current_directory_entry_ = 0;
  147. struct dirent* dent;
  148. // NOTE: Per the readdir() documentation, when the end of the directory is
  149. // reached with no errors, null is returned and errno is not changed.
  150. // Therefore we must reset errno to zero before calling readdir() if we
  151. // wish to know whether a null result indicates an error condition.
  152. while (errno = 0, dent = readdir(dir)) {
  153. FileInfo info;
  154. info.filename_ = FilePath(dent->d_name);
  155. if (ShouldSkip(info.filename_))
  156. continue;
  157. const bool is_pattern_matched = IsPatternMatched(info.filename_);
  158. // MATCH_ONLY policy enumerates files and directories which matching
  159. // pattern only. So we can early skip further checks.
  160. if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY &&
  161. !is_pattern_matched)
  162. continue;
  163. // Do not call OS stat/lstat if there is no sense to do it. If pattern is
  164. // not matched (file will not appear in results) and search is not
  165. // recursive (possible directory will not be added to pending paths) -
  166. // there is no sense to obtain item below.
  167. if (!recursive_ && !is_pattern_matched)
  168. continue;
  169. const FilePath full_path = root_path_.Append(info.filename_);
  170. GetStat(full_path, ShouldShowSymLinks(file_type_), &info.stat_);
  171. const bool is_dir = info.IsDirectory();
  172. // Recursive mode: schedule traversal of a directory if either
  173. // SHOW_SYM_LINKS is on or we haven't visited the directory yet.
  174. if (recursive_ && is_dir &&
  175. (!ShouldTrackVisitedDirectories(file_type_) ||
  176. visited_directories_.insert(info.stat_.st_ino).second)) {
  177. pending_paths_.push(full_path);
  178. }
  179. if (is_pattern_matched && IsTypeMatched(is_dir))
  180. directory_entries_.push_back(std::move(info));
  181. }
  182. int readdir_errno = errno;
  183. closedir(dir);
  184. if (readdir_errno != 0 && error_policy_ != ErrorPolicy::IGNORE_ERRORS) {
  185. error_ = File::OSErrorToFileError(readdir_errno);
  186. return FilePath();
  187. }
  188. // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern.
  189. // ALL policy enumerates files in all subfolders by origin pattern.
  190. if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY)
  191. pattern_.clear();
  192. }
  193. return root_path_.Append(
  194. directory_entries_[current_directory_entry_].filename_);
  195. }
  196. FileEnumerator::FileInfo FileEnumerator::GetInfo() const {
  197. return directory_entries_[current_directory_entry_];
  198. }
  199. bool FileEnumerator::IsPatternMatched(const FilePath& path) const {
  200. return pattern_.empty() ||
  201. !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE);
  202. }
  203. } // namespace base