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) && \ | |
102 !defined(OS_SOLARIS) && !defined(OS_ANDROID) && !defined(OS_AIX) && \ | |
103 !defined(OS_FUCHSIA) | |
104 #error Port warning: depending on the definition of struct dirent, \ | |
105 additional space for pathname may be needed | |
106 #endif | |
107 | |
77 ++current_directory_entry_; | 108 ++current_directory_entry_; |
78 | 109 |
79 // While we've exhausted the entries in the current directory, do the next | 110 // While we've exhausted the entries in the current directory, do the next |
80 while (current_directory_entry_ >= directory_entries_.size()) { | 111 while (current_directory_entry_ >= directory_entries_.size()) { |
81 if (pending_paths_.empty()) | 112 if (pending_paths_.empty()) |
82 return FilePath(); | 113 return FilePath(); |
83 | 114 |
84 root_path_ = pending_paths_.top(); | 115 root_path_ = pending_paths_.top(); |
85 root_path_ = root_path_.StripTrailingSeparators(); | 116 root_path_ = root_path_.StripTrailingSeparators(); |
86 pending_paths_.pop(); | 117 pending_paths_.pop(); |
87 | 118 |
88 std::vector<FileInfo> entries; | 119 DIR* dir = opendir(root_path_.value().c_str()); |
89 if (!ReadDirectory(&entries, root_path_, file_type_ & SHOW_SYM_LINKS)) | 120 if (!dir) |
90 continue; | 121 continue; |
91 | 122 |
92 directory_entries_.clear(); | 123 directory_entries_.clear(); |
93 current_directory_entry_ = 0; | 124 current_directory_entry_ = 0; |
94 for (std::vector<FileInfo>::const_iterator i = entries.begin(); | 125 struct dirent dent_buf; |
95 i != entries.end(); ++i) { | 126 struct dirent* dent; |
96 FilePath full_path = root_path_.Append(i->filename_); | 127 while (readdir_r(dir, &dent_buf, &dent) == 0 && dent) { |
scottmg
2017/06/29 23:44:21
Please make sure the readdir_r->readdir, and the "
ivafanas
2017/06/30 04:57:53
Done.
| |
97 if (ShouldSkip(full_path)) | 128 FileInfo info; |
129 info.filename_ = FilePath(dent->d_name); | |
130 | |
131 if (ShouldSkip(info.filename_)) | |
98 continue; | 132 continue; |
99 | 133 |
100 if (pattern_.size() && | 134 const bool is_pattern_matched = IsPatternMatched(info.filename_); |
101 fnmatch(pattern_.c_str(), full_path.value().c_str(), FNM_NOESCAPE)) | 135 |
136 // MATCH_ONLY policy enumerates files and directories which matching | |
137 // pattern only. So we can early skip further checks. | |
138 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY && | |
139 !is_pattern_matched) | |
102 continue; | 140 continue; |
103 | 141 |
104 if (recursive_ && S_ISDIR(i->stat_.st_mode)) | 142 // Do not call OS stat/lstat if there is no sense to do it. If pattern is |
143 // not matched (file will not appear in results) and search is not | |
144 // recursive (possible directory will not be added to pending paths) - | |
145 // there is no sense to obtain item below. | |
146 if (!recursive_ && !is_pattern_matched) | |
147 continue; | |
148 | |
149 const FilePath full_path = root_path_.Append(info.filename_); | |
150 GetStat(full_path, file_type_ & SHOW_SYM_LINKS, &info.stat_); | |
151 | |
152 const bool is_dir = S_ISDIR(info.stat_.st_mode); | |
153 | |
154 if (recursive_ && is_dir) | |
105 pending_paths_.push(full_path); | 155 pending_paths_.push(full_path); |
106 | 156 |
107 if ((S_ISDIR(i->stat_.st_mode) && (file_type_ & DIRECTORIES)) || | 157 if (is_pattern_matched && IsTypeMatched(is_dir)) |
108 (!S_ISDIR(i->stat_.st_mode) && (file_type_ & FILES))) | 158 directory_entries_.push_back(std::move(info)); |
109 directory_entries_.push_back(*i); | |
110 } | 159 } |
160 closedir(dir); | |
161 | |
162 // MATCH_ONLY policy enumerates files in matched subfolders by "*" pattern. | |
163 // ALL policy enumerates files in all subfolders by origin pattern. | |
164 if (folder_search_policy_ == FolderSearchPolicy::MATCH_ONLY) | |
165 pattern_.clear(); | |
111 } | 166 } |
112 | 167 |
113 return root_path_.Append( | 168 return root_path_.Append( |
114 directory_entries_[current_directory_entry_].filename_); | 169 directory_entries_[current_directory_entry_].filename_); |
115 } | 170 } |
116 | 171 |
117 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { | 172 FileEnumerator::FileInfo FileEnumerator::GetInfo() const { |
118 return directory_entries_[current_directory_entry_]; | 173 return directory_entries_[current_directory_entry_]; |
119 } | 174 } |
120 | 175 |
121 bool FileEnumerator::ReadDirectory(std::vector<FileInfo>* entries, | 176 bool FileEnumerator::IsPatternMatched(const FilePath& path) const { |
122 const FilePath& source, bool show_links) { | 177 return pattern_.empty() || |
123 base::ThreadRestrictions::AssertIOAllowed(); | 178 !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 } | 179 } |
162 | 180 |
163 } // namespace base | 181 } // namespace base |
OLD | NEW |