| 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
|
|
|