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, bool show_links, struct stat* st) { | |
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)); | |
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) && \ | |
Lei Zhang
2017/06/08 08:21:18
Did this list get shorter?
ivafanas
2017/06/16 11:13:15
Done.
| |
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 pending 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, file_type_ & SHOW_SYM_LINKS, &info.stat_); | |
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) |
Lei Zhang
2017/06/08 08:21:18
You can check |is_pattern_matched| first, and if t
ivafanas
2017/06/16 11:13:15
Done.
| |
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 |