Chromium Code Reviews| Index: base/files/file_enumerator_posix.cc |
| diff --git a/base/files/file_enumerator_posix.cc b/base/files/file_enumerator_posix.cc |
| index 2b54c6dcb3bccb39c123c57e21c22f3b2c375628..982ca1e16a677b7ca1c03b244ef8c61e4ab05682 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, struct stat* st, bool show_links) { |
| + 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)); |
|
Mark Mentovai
2017/05/19 17:16:11
Why this instead of a failure return value?
ivafanas
2017/05/22 04:42:28
I've just saved the previous behaviour:
https://cs
|
| + } |
| +} |
| + |
| +} // 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,14 @@ FileEnumerator::~FileEnumerator() { |
| } |
| FilePath FileEnumerator::Next() { |
| + base::ThreadRestrictions::AssertIOAllowed(); |
| + |
| +#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ |
| + !defined(OS_SOLARIS) && !defined(OS_ANDROID) |
| +#error Port warning: depending on the definition of struct dirent, \ |
| + additional space for pathname may be needed |
| +#endif |
| + |
| ++current_directory_entry_; |
| // While we've exhausted the entries in the current directory, do the next |
| @@ -85,29 +115,53 @@ 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(); |
| 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_buf; |
| + struct dirent* dent; |
| + while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
| + 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 penfing 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, &info.stat_, file_type_ & SHOW_SYM_LINKS); |
| + |
| + 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 (IsTypeMatched(is_dir) && is_pattern_matched) |
| + 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,46 +172,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_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ |
| - !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(OS_AIX) && \ |
| - !defined(OS_FUCHSIA) |
| -#error Port warning: depending on the definition of struct dirent, \ |
| - additional space for pathname may be needed |
| -#endif |
| - |
| - struct dirent dent_buf; |
| - struct dirent* dent; |
| - while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
| - 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 |