Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/files/file_enumerator.h" | 5 #include "base/files/file_enumerator.h" |
| 6 | 6 |
| 7 #include <dirent.h> | 7 #include <dirent.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <fnmatch.h> | 9 #include <fnmatch.h> |
| 10 #include <stdint.h> | 10 #include <stdint.h> |
| 11 #include <string.h> | |
| 11 | 12 |
| 12 #include "base/logging.h" | 13 #include "base/logging.h" |
| 13 #include "base/threading/thread_restrictions.h" | 14 #include "base/threading/thread_restrictions.h" |
| 14 #include "build/build_config.h" | 15 #include "build/build_config.h" |
| 15 | 16 |
| 16 namespace base { | 17 namespace base { |
| 18 namespace { | |
| 19 | |
| 20 void GetStat(const FilePath& path, struct stat* st, bool show_links) { | |
| 21 DCHECK(st); | |
| 22 const int res = show_links ? lstat(path.value().c_str(), st) | |
| 23 : stat(path.value().c_str(), st); | |
| 24 if (res < 0) { | |
| 25 // Print the stat() error message unless it was ENOENT and we're following | |
| 26 // symlinks. | |
| 27 if (!(errno == ENOENT && !show_links)) | |
| 28 DPLOG(ERROR) << "Couldn't stat" << path.value(); | |
| 29 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
| |
| 30 } | |
| 31 } | |
| 32 | |
| 33 } // namespace | |
| 17 | 34 |
| 18 // FileEnumerator::FileInfo ---------------------------------------------------- | 35 // FileEnumerator::FileInfo ---------------------------------------------------- |
| 19 | 36 |
| 20 FileEnumerator::FileInfo::FileInfo() { | 37 FileEnumerator::FileInfo::FileInfo() { |
| 21 memset(&stat_, 0, sizeof(stat_)); | 38 memset(&stat_, 0, sizeof(stat_)); |
| 22 } | 39 } |
| 23 | 40 |
| 24 bool FileEnumerator::FileInfo::IsDirectory() const { | 41 bool FileEnumerator::FileInfo::IsDirectory() const { |
| 25 return S_ISDIR(stat_.st_mode); | 42 return S_ISDIR(stat_.st_mode); |
| 26 } | 43 } |
| 27 | 44 |
| 28 FilePath FileEnumerator::FileInfo::GetName() const { | 45 FilePath FileEnumerator::FileInfo::GetName() const { |
| 29 return filename_; | 46 return filename_; |
| 30 } | 47 } |
| 31 | 48 |
| 32 int64_t FileEnumerator::FileInfo::GetSize() const { | 49 int64_t FileEnumerator::FileInfo::GetSize() const { |
| 33 return stat_.st_size; | 50 return stat_.st_size; |
| 34 } | 51 } |
| 35 | 52 |
| 36 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { | 53 base::Time FileEnumerator::FileInfo::GetLastModifiedTime() const { |
| 37 return base::Time::FromTimeT(stat_.st_mtime); | 54 return base::Time::FromTimeT(stat_.st_mtime); |
| 38 } | 55 } |
| 39 | 56 |
| 40 // FileEnumerator -------------------------------------------------------------- | 57 // FileEnumerator -------------------------------------------------------------- |
| 41 | 58 |
| 42 FileEnumerator::FileEnumerator(const FilePath& root_path, | 59 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 43 bool recursive, | 60 bool recursive, |
| 44 int file_type) | 61 int file_type) |
| 45 : current_directory_entry_(0), | 62 : FileEnumerator(root_path, |
| 46 root_path_(root_path), | 63 recursive, |
| 47 recursive_(recursive), | 64 file_type, |
| 48 file_type_(file_type) { | 65 FilePath::StringType(), |
| 49 // INCLUDE_DOT_DOT must not be specified if recursive. | 66 FolderSearchPolicy::MATCH_ONLY) {} |
| 50 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | |
| 51 pending_paths_.push(root_path); | |
| 52 } | |
| 53 | 67 |
| 54 FileEnumerator::FileEnumerator(const FilePath& root_path, | 68 FileEnumerator::FileEnumerator(const FilePath& root_path, |
| 55 bool recursive, | 69 bool recursive, |
| 56 int file_type, | 70 int file_type, |
| 57 const FilePath::StringType& pattern) | 71 const FilePath::StringType& pattern) |
| 72 : FileEnumerator(root_path, | |
| 73 recursive, | |
| 74 file_type, | |
| 75 pattern, | |
| 76 FolderSearchPolicy::MATCH_ONLY) {} | |
| 77 | |
| 78 FileEnumerator::FileEnumerator(const FilePath& root_path, | |
| 79 bool recursive, | |
| 80 int file_type, | |
| 81 const FilePath::StringType& pattern, | |
| 82 FolderSearchPolicy folder_search_policy) | |
| 58 : current_directory_entry_(0), | 83 : current_directory_entry_(0), |
| 59 root_path_(root_path), | 84 root_path_(root_path), |
| 60 recursive_(recursive), | 85 recursive_(recursive), |
| 61 file_type_(file_type), | 86 file_type_(file_type), |
| 62 pattern_(root_path.Append(pattern).value()) { | 87 pattern_(pattern), |
| 88 folder_search_policy_(folder_search_policy) { | |
| 63 // INCLUDE_DOT_DOT must not be specified if recursive. | 89 // INCLUDE_DOT_DOT must not be specified if recursive. |
| 64 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); | 90 DCHECK(!(recursive && (INCLUDE_DOT_DOT & file_type_))); |
| 65 // The Windows version of this code appends the pattern to the root_path, | 91 |
| 66 // potentially only matching against items in the top-most directory. | |
| 67 // Do the same here. | |
| 68 if (pattern.empty()) | |
| 69 pattern_ = FilePath::StringType(); | |
| 70 pending_paths_.push(root_path); | 92 pending_paths_.push(root_path); |
| 71 } | 93 } |
| 72 | 94 |
| 73 FileEnumerator::~FileEnumerator() { | 95 FileEnumerator::~FileEnumerator() { |
| 74 } | 96 } |
| 75 | 97 |
| 76 FilePath FileEnumerator::Next() { | 98 FilePath FileEnumerator::Next() { |
| 99 base::ThreadRestrictions::AssertIOAllowed(); | |
| 100 | |
| 101 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ | |
| 102 !defined(OS_SOLARIS) && !defined(OS_ANDROID) | |
| 103 #error Port warning: depending on the definition of struct dirent, \ | |
| 104 additional space for pathname may be needed | |
| 105 #endif | |
| 106 | |
| 77 ++current_directory_entry_; | 107 ++current_directory_entry_; |
| 78 | 108 |
| 79 // While we've exhausted the entries in the current directory, do the next | 109 // While we've exhausted the entries in the current directory, do the next |
| 80 while (current_directory_entry_ >= directory_entries_.size()) { | 110 while (current_directory_entry_ >= directory_entries_.size()) { |
| 81 if (pending_paths_.empty()) | 111 if (pending_paths_.empty()) |
| 82 return FilePath(); | 112 return FilePath(); |
| 83 | 113 |
| 84 root_path_ = pending_paths_.top(); | 114 root_path_ = pending_paths_.top(); |
| 85 root_path_ = root_path_.StripTrailingSeparators(); | 115 root_path_ = root_path_.StripTrailingSeparators(); |
| 86 pending_paths_.pop(); | 116 pending_paths_.pop(); |
| 87 | 117 |
| 88 std::vector<FileInfo> entries; | 118 DIR* dir = opendir(root_path_.value().c_str()); |
| 89 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) | 119 if (!dir) |
| 90 continue; | 120 continue; |
| 91 | 121 |
| 92 directory_entries_.clear(); | 122 directory_entries_.clear(); |
| 93 current_directory_entry_ = 0; | 123 current_directory_entry_ = 0; |
| 94 for (std::vector<FileInfo>::const_iterator i = entries.begin(); | 124 struct dirent dent_buf; |
| 95 i != entries.end(); ++i) { | 125 struct dirent* dent; |
| 96 FilePath full_path = root_path_.Append(i->filename_); | 126 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
| 97 if (ShouldSkip(full_path)) | 127 FileInfo info; |
| 128 info.filename_ = FilePath(dent->d_name); | |
| 129 | |
| 130 if (ShouldSkip(info.filename_)) | |
| 98 continue; | 131 continue; |
| 99 | 132 |
| 100 if (pattern_.size() && | 133 const bool is_pattern_matched = IsPatternMatched(info.filename_); |
| 101 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) | 134 |
| 135 // MATCH_ONLY policy enumerates files and directories which matching | |
| 136 // pattern only. So we can early skip further checks. | |
| 137 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY && | |
| 138 !is_pattern_matched) | |
| 102 continue; | 139 continue; |
| 103 | 140 |
| 104 if (recursive_ && S_ISDIR(i->stat_.st_mode)) | 141 // Do not call OS stat/lstat if there is no sense to do it. If pattern is |
| 142 // not matched (file will not appear in results) and search is not | |
| 143 // recursive (possible directory will not be added to penfing paths) - | |
| 144 // there is no sense to obtain item below. | |
| 145 if (!recursive_ && !is_pattern_matched) | |
| 146 continue; | |
| 147 | |
| 148 const FilePath full_path = root_path_.Append(info.filename_); | |
| 149 GetStat(full_path, &info.stat_, file_type_ & SHOW_SYM_LINKS); | |
| 150 | |
| 151 const bool is_dir = S_ISDIR(info.stat_.st_mode); | |
| 152 | |
| 153 if (recursive_ && is_dir) | |
| 105 pending_paths_.push(full_path); | 154 pending_paths_.push(full_path); |
| 106 | 155 |
| 107 if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) || | 156 if (IsTypeMatched(is_dir) && is_pattern_matched) |
| 108 (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES))) | 157 directory_entries_.push_back(std::move(info)); |
| 109 directory_entries_.push_back(*i); | |
| 110 } | 158 } |
| 159 closedir(dir); | |
| 160 | |
| 161 // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern. | |
| 162 // ALL policy enumerates files in all subfolders by origin pattern. | |
| 163 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) | |
| 164 pattern_.clear(); | |
| 111 } | 165 } |
| 112 | 166 |
| 113 return root_path_.Append( | 167 return root_path_.Append( |
| 114 directory_entries_[current_directory_entry_].filename_); | 168 directory_entries_[current_directory_entry_].filename_); |
| 115 } | 169 } |
| 116 | 170 |
| 117 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { | 171 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { |
| 118 return directory_entries_[current_directory_entry_]; | 172 return directory_entries_[current_directory_entry_]; |
| 119 } | 173 } |
| 120 | 174 |
| 121 bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries, | 175 bool FileEnumerator::IsPatternMatched(const FilePath& path) const { |
| 122 const FilePath& source, bool show_links) { | 176 return pattern_.empty() || |
| 123 base::ThreadRestrictions::AssertIOAllowed(); | 177 !fnmatch(pattern_.c_str(), path.value().c_str(), FNM_NOESCAPE); |
| 124 DIR* dir = opendir(source.value().c_str()); | |
| 125 if (!dir) | |
| 126 return false; | |
| 127 | |
| 128 #if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_BSD) && \ | |
| 129 !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(OS_AIX) && \ | |
| 130 !defined(OS_FUCHSIA) | |
| 131 #error Port warning: depending on the definition of struct dirent, \ | |
| 132 additional space for pathname may be needed | |
| 133 #endif | |
| 134 | |
| 135 struct dirent dent_buf; | |
| 136 struct dirent* dent; | |
| 137 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { | |
| 138 FileInfo info; | |
| 139 info.filename_ = FilePath(dent->d_name); | |
| 140 | |
| 141 FilePath full_name = source.Append(dent->d_name); | |
| 142 int ret; | |
| 143 if (show_links) | |
| 144 ret = lstat(full_name.value().c_str(), &info.stat_); | |
| 145 else | |
| 146 ret = stat(full_name.value().c_str(), &info.stat_); | |
| 147 if (ret < 0) { | |
| 148 // Print the stat() error message unless it was ENOENT and we're | |
| 149 // following symlinks. | |
| 150 if (!(errno == ENOENT && !show_links)) { | |
| 151 DPLOG(ERROR) << "Couldn't stat " | |
| 152 << source.Append(dent->d_name).value(); | |
| 153 } | |
| 154 memset(&info.stat_, 0, sizeof(info.stat_)); | |
| 155 } | |
| 156 entries->push_back(info); | |
| 157 } | |
| 158 | |
| 159 closedir(dir); | |
| 160 return true; | |
| 161 } | 178 } |
| 162 | 179 |
| 163 } // namespace base | 180 } // namespace base |
| OLD | NEW |