Index: base/files/file_enumerator_posix.cc |
diff --git a/base/files/file_enumerator_posix.cc b/base/files/file_enumerator_posix.cc |
index a1b4949c796a5d242d77329039c476ecc5fdbf62..bc5c3cc5498062f3a37d4d491e130e8075441c85 100644 |
--- a/base/files/file_enumerator_posix.cc |
+++ b/base/files/file_enumerator_posix.cc |
@@ -8,12 +8,29 @@ |
#include <errno.h> |
#include <fnmatch.h> |
#include <stdint.h> |
+#include <string.h> |
#include "base/logging.h" |
#include "base/threading/thread_restrictions.h" |
#include "build/build_config.h" |
namespace base { |
+namespace { |
+ |
+void GetStat(const FilePath& path, bool show_links, struct stat* st) { |
+ DCHECK(st); |
+ const int res = show_links ? lstat(path.value().c_str(), st) |
+ : stat(path.value().c_str(), st); |
+ if (res < 0) { |
+ // Print the stat() error message unless it was ENOENT and we're following |
+ // symlinks. |
+ if (!(errno == ENOENT && !show_links)) |
+ DPLOG(ERROR) << "Couldn't stat" << path.value(); |
+ memset(st, 0, sizeof(*st)); |
+ } |
+} |
+ |
+} // namespace |
// FileEnumerator::FileInfo ---------------------------------------------------- |
@@ -42,31 +59,36 @@ base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { |
FileEnumerator::FileEnumerator(const FilePath& root_path, |
bool recursive, |
int file_type) |
- : current_directory_entry_(0), |
- root_path_(root_path), |
- recursive_(recursive), |
- file_type_(file_type) { |
- // INCLUDE_DOT_DOT must not be specified if recursive. |
- DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
- pending_paths_.push(root_path); |
-} |
+ : FileEnumerator(root_path, |
+ recursive, |
+ file_type, |
+ FilePath::StringType(), |
+ FolderSearchPolicy::MATCH_ONLY) {} |
FileEnumerator::FileEnumerator(const FilePath& root_path, |
bool recursive, |
int file_type, |
const FilePath::StringType& pattern) |
+ : FileEnumerator(root_path, |
+ recursive, |
+ file_type, |
+ pattern, |
+ FolderSearchPolicy::MATCH_ONLY) {} |
+ |
+FileEnumerator::FileEnumerator(const FilePath& root_path, |
+ bool recursive, |
+ int file_type, |
+ const FilePath::StringType& pattern, |
+ FolderSearchPolicy folder_search_policy) |
: current_directory_entry_(0), |
root_path_(root_path), |
recursive_(recursive), |
file_type_(file_type), |
- pattern_(root_path.Append(pattern).value()) { |
+ pattern_(pattern), |
+ folder_search_policy_(folder_search_policy) { |
// INCLUDE_DOT_DOT must not be specified if recursive. |
DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
- // The Windows version of this code appends the pattern to the root_path, |
- // potentially only matching against items in the top-most directory. |
- // Do the same here. |
- if (pattern.empty()) |
- pattern_ = FilePath::StringType(); |
+ |
pending_paths_.push(root_path); |
} |
@@ -74,6 +96,8 @@ FileEnumerator::~FileEnumerator() { |
} |
FilePath FileEnumerator::Next() { |
+ base::ThreadRestrictions::AssertIOAllowed(); |
+ |
++current_directory_entry_; |
// While we've exhausted the entries in the current directory, do the next |
@@ -85,29 +109,66 @@ FilePath FileEnumerator::Next() { |
root_path_ = root_path_.StripTrailingSeparators(); |
pending_paths_.pop(); |
- std::vector<FileInfo> entries; |
- if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) |
+ DIR* dir = opendir(root_path_.value().c_str()); |
+ if (!dir) |
continue; |
directory_entries_.clear(); |
+ |
+#if defined(OS_FUCHSIA) |
+ // Fuchsia does not support .. on the file system server side, see |
+ // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and |
+ // https://crbug.com/735540. However, for UI purposes, having the parent |
+ // directory show up in directory listings makes sense, so we add it here to |
+ // match the expectation on other operating systems. In cases where this |
+ // is useful it should be resolvable locally. |
+ FileInfo dotdot; |
+ dotdot.stat_.st_mode = S_IFDIR; |
+ dotdot.filename_ = FilePath(".."); |
+ directory_entries_->push_back(dotdot); |
+#endif // OS_FUCHSIA |
+ |
current_directory_entry_ = 0; |
- for (std::vector<FileInfo>::const_iterator i = entries.begin(); |
- i != entries.end(); ++i) { |
- FilePath full_path = root_path_.Append(i->filename_); |
- if (ShouldSkip(full_path)) |
+ struct dirent* dent; |
+ while ((dent = readdir(dir))) { |
+ FileInfo info; |
+ info.filename_ = FilePath(dent->d_name); |
+ |
+ if (ShouldSkip(info.filename_)) |
continue; |
- if (pattern_.size() && |
- fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) |
+ const bool is_pattern_matched = IsPatternMatched(info.filename_); |
+ |
+ // MATCH_ONLY policy enumerates files and directories which matching |
+ // pattern only. So we can early skip further checks. |
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY && |
+ !is_pattern_matched) |
continue; |
- if (recursive_ && S_ISDIR(i->stat_.st_mode)) |
+ // Do not call OS stat/lstat if there is no sense to do it. If pattern is |
+ // not matched (file will not appear in results) and search is not |
+ // recursive (possible directory will not be added to pending paths) - |
+ // there is no sense to obtain item below. |
+ if (!recursive_ && !is_pattern_matched) |
+ continue; |
+ |
+ const FilePath full_path = root_path_.Append(info.filename_); |
+ GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_); |
+ |
+ const bool is_dir = S_ISDIR(info.stat_.st_mode); |
+ |
+ if (recursive_ && is_dir) |
pending_paths_.push(full_path); |
- if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) || |
- (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES))) |
- directory_entries_.push_back(*i); |
+ if (is_pattern_matched && IsTypeMatched(is_dir)) |
+ directory_entries_.push_back(std::move(info)); |
} |
+ closedir(dir); |
+ |
+ // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern. |
+ // ALL policy enumerates files in all subfolders by origin pattern. |
+ if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) |
+ pattern_.clear(); |
} |
return root_path_.Append( |
@@ -118,51 +179,9 @@ FileEnumerator::FileInfo FileEnumerator::GetInfo() const { |
return directory_entries_[current_directory_entry_]; |
} |
-bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries, |
- const FilePath& source, bool show_links) { |
- base::ThreadRestrictions::AssertIOAllowed(); |
- DIR* dir = opendir(source.value().c_str()); |
- if (!dir) |
- return false; |
- |
-#if defined(OS_FUCHSIA) |
- // Fuchsia does not support .. on the file system server side, see |
- // https://fuchsia.googlesource.com/docs/+/master/dotdot.md and |
- // https://crbug.com/735540. However, for UI purposes, having the parent |
- // directory show up in directory listings makes sense, so we add it here to |
- // match the expectation on other operating systems. In cases where this |
- // is useful it should be resolvable locally. |
- FileInfo dotdot; |
- dotdot.stat_.st_mode = S_IFDIR; |
- dotdot.filename_ = FilePath(".."); |
- entries->push_back(dotdot); |
-#endif // OS_FUCHSIA |
- |
- struct dirent* dent; |
- while ((dent = readdir(dir))) { |
- FileInfo info; |
- info.filename_ = FilePath(dent->d_name); |
- |
- FilePath full_name = source.Append(dent->d_name); |
- int ret; |
- if (show_links) |
- ret = lstat(full_name.value().c_str(), &info.stat_); |
- else |
- ret = stat(full_name.value().c_str(), &info.stat_); |
- if (ret < 0) { |
- // Print the stat() error message unless it was ENOENT and we're |
- // following symlinks. |
- if (!(errno == ENOENT && !show_links)) { |
- DPLOG(ERROR) << "Couldn't stat " |
- << source.Append(dent->d_name).value(); |
- } |
- memset(&info.stat_, 0, sizeof(info.stat_)); |
- } |
- entries->push_back(info); |
- } |
- |
- closedir(dir); |
- return true; |
+bool FileEnumerator::IsPatternMatched(const FilePath& path) const { |
+ return pattern_.empty() || |
+ !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE); |
} |
} // namespace base |